From eef614236af12f3f6a462a08e17346bc1677a8f0 Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Wed, 18 Mar 2026 22:29:45 +0000 Subject: [PATCH 01/24] rename `DefaultSearch` to `StandardSearch` --- Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs | 6 +++--- .../Search/{DefaultSearch.cs => StandardSearch.cs} | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) rename Flow.Launcher.Plugin.OneNote/Search/{DefaultSearch.cs => StandardSearch.cs} (76%) diff --git a/Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs b/Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs index ed56c56..b14e1d3 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs @@ -6,7 +6,7 @@ public class SearchManager { private readonly TitleSearch titleSearch; private readonly NotebookExplorer notebookExplorer; - private readonly DefaultSearch defaultSearch; + private readonly StandardSearch standardSearch; private readonly RecentPages recentPages; private readonly RootCache rootCache; public RootCache RootCache => rootCache; @@ -17,7 +17,7 @@ public SearchManager(PluginInitContext context, Settings settings, ResultCreator titleSearch = new TitleSearch(context, settings, resultCreator, rootCache); notebookExplorer = new NotebookExplorer(context, settings, resultCreator, titleSearch, rootCache); recentPages = new RecentPages(context, settings, resultCreator, rootCache); - defaultSearch = new DefaultSearch(context, settings, resultCreator); + standardSearch = new StandardSearch(context, settings, resultCreator); } public List Query(Query query) @@ -32,7 +32,7 @@ public List Query(Query query) { } when search.StartsWithOrd(titleSearch.keyword) => titleSearch.GetResults(query), { } when search.StartsWithOrd(notebookExplorer.keyword) => notebookExplorer.GetResults(query), { } when search.StartsWithOrd(recentPages.keyword) => recentPages.GetResults(query), - _ => defaultSearch.GetResults(query), + _ => standardSearch.GetResults(query), }; } } diff --git a/Flow.Launcher.Plugin.OneNote/Search/DefaultSearch.cs b/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs similarity index 76% rename from Flow.Launcher.Plugin.OneNote/Search/DefaultSearch.cs rename to Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs index 150042f..4839679 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/DefaultSearch.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs @@ -4,13 +4,13 @@ namespace Flow.Launcher.Plugin.OneNote.Search { - public class DefaultSearch(PluginInitContext context, Settings settings, ResultCreator resultCreator) + public class StandardSearch(PluginInitContext context, Settings settings, ResultCreator resultCreator) : SearchBase(context, settings, resultCreator, Keyword.Empty) { public override List GetResults(Query query) { string search = query.Search; - if (!char.IsLetterOrDigit(search[0])) + if (!char.IsLetterOrDigit(search[keyword.Length])) { return resultCreator.InvalidQuery(); } From 08b7f4617038b88ea1d94156b85d1390e20172d4 Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Wed, 18 Mar 2026 23:28:21 +0000 Subject: [PATCH 02/24] refactor standard and scope search --- .../Search/NotebookExplorer.cs | 20 ++----------- .../Search/StandardSearch.cs | 29 +++++++++++++------ 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs b/Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs index d7e5efc..5465a8e 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs @@ -10,6 +10,7 @@ namespace Flow.Launcher.Plugin.OneNote.Search public class NotebookExplorer(PluginInitContext context, Settings settings, ResultCreator resultCreator, TitleSearch titleSearch, RootCache rootCache) : SearchBase(context, settings, resultCreator, settings.Keywords.NotebookExplorer) { + private readonly StandardSearch standardSearch = new(context, settings, resultCreator); public override List GetResults(Query query) { if (!ValidateSearch(query, out string? search, out IOneNoteItem? parent, out IEnumerable collection)) @@ -18,7 +19,7 @@ public override List GetResults(Query query) List results = search switch { { } when search.StartsWithOrd(Keywords.TitleSearch) && parent is not Page => titleSearch.Filter(search, parent, collection), - { } when search.StartsWithOrd(Keywords.ScopedSearch) && parent is INotebookOrSectionGroup => ScopedSearch(search, parent), + { } when search.StartsWithOrd(Keywords.ScopedSearch) && parent is INotebookOrSectionGroup => standardSearch.GetResults(query, parent), { } when !string.IsNullOrWhiteSpace(search) => Explorer(search, parent, collection), _ => ShowAll(parent, collection), }; @@ -71,23 +72,6 @@ private List ShowAll(IOneNoteItem? parent, IEnumerable col return results.Count != 0 ? results : resultCreator.EmptyCollection(results, parent); } - private List ScopedSearch(string query, IOneNoteItem parent) - { - if (query.Length == Keywords.ScopedSearch.Length) - return resultCreator.SearchType("Now searching all pages", parent.Name); - - if (!char.IsLetterOrDigit(query[Keywords.ScopedSearch.Length])) - return resultCreator.InvalidQuery(); - - string currentSearch = query[Keywords.ScopedSearch.Length..]; - - var results = OneNoteApp.FindPages(currentSearch, parent) - .Select(pg => resultCreator.CreatePageResult(pg, currentSearch)) - .ToList(); - - return results.Count != 0 ? results : ResultCreator.NoMatchesFound(); - } - private List Explorer(string search, IOneNoteItem? parent, IEnumerable collection) { var results = collection.FilterBySettings(settings) diff --git a/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs b/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs index 4839679..6daffc3 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using LinqToOneNote; using OneNoteApp = LinqToOneNote.OneNote; namespace Flow.Launcher.Plugin.OneNote.Search @@ -7,17 +8,27 @@ namespace Flow.Launcher.Plugin.OneNote.Search public class StandardSearch(PluginInitContext context, Settings settings, ResultCreator resultCreator) : SearchBase(context, settings, resultCreator, Keyword.Empty) { - public override List GetResults(Query query) + public override List GetResults(Query query) => Filter(query.Search, null, keyword); + public List GetResults(Query query, IOneNoteItem? parent) => Filter(query.Search, parent, settings.Keywords.ScopedSearch); + + private List Filter(string query, IOneNoteItem? parent, Keyword keyword) { - string search = query.Search; - if (!char.IsLetterOrDigit(search[keyword.Length])) - { + if (query.Length == keyword.Length) + return resultCreator.SearchType("Now searching all pages", parent?.Name); + + if (!char.IsLetterOrDigit(query[keyword.Length])) return resultCreator.InvalidQuery(); - } - - return OneNoteApp.FindPages(search) - .Select(pg => resultCreator.CreatePageResult(pg, search)) - .ToList(); + + string currentSearch = query[keyword.Length..]; + + IEnumerable pages = parent is null + ? OneNoteApp.FindPages(currentSearch) + : OneNoteApp.FindPages(currentSearch, parent); + var results = pages.Select(pg => resultCreator.CreatePageResult(pg, currentSearch)) + .ToList(); + + return results.Count != 0 ? results : ResultCreator.NoMatchesFound(); } + } } \ No newline at end of file From d68eb890be2999630340f0eca498e2a1885f3f17 Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Wed, 18 Mar 2026 23:31:03 +0000 Subject: [PATCH 03/24] refactor notebook explorer --- Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs | 5 +++-- Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs | 2 +- Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs b/Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs index 5465a8e..5b6971e 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs @@ -7,9 +7,10 @@ namespace Flow.Launcher.Plugin.OneNote.Search { - public class NotebookExplorer(PluginInitContext context, Settings settings, ResultCreator resultCreator, TitleSearch titleSearch, RootCache rootCache) + public class NotebookExplorer(PluginInitContext context, Settings settings, ResultCreator resultCreator, RootCache rootCache) : SearchBase(context, settings, resultCreator, settings.Keywords.NotebookExplorer) { + private readonly TitleSearch titleSearch = new (context, settings, resultCreator, rootCache); private readonly StandardSearch standardSearch = new(context, settings, resultCreator); public override List GetResults(Query query) { @@ -18,7 +19,7 @@ public override List GetResults(Query query) List results = search switch { - { } when search.StartsWithOrd(Keywords.TitleSearch) && parent is not Page => titleSearch.Filter(search, parent, collection), + { } when search.StartsWithOrd(Keywords.TitleSearch) && parent is not Page => titleSearch.GetResults(search, parent, collection), { } when search.StartsWithOrd(Keywords.ScopedSearch) && parent is INotebookOrSectionGroup => standardSearch.GetResults(query, parent), { } when !string.IsNullOrWhiteSpace(search) => Explorer(search, parent, collection), _ => ShowAll(parent, collection), diff --git a/Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs b/Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs index b14e1d3..bb371e8 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs @@ -15,7 +15,7 @@ public SearchManager(PluginInitContext context, Settings settings, ResultCreator { rootCache = new RootCache(); titleSearch = new TitleSearch(context, settings, resultCreator, rootCache); - notebookExplorer = new NotebookExplorer(context, settings, resultCreator, titleSearch, rootCache); + notebookExplorer = new NotebookExplorer(context, settings, resultCreator, rootCache); recentPages = new RecentPages(context, settings, resultCreator, rootCache); standardSearch = new StandardSearch(context, settings, resultCreator); } diff --git a/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs b/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs index a6649fe..9f3ac14 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs @@ -7,9 +7,9 @@ namespace Flow.Launcher.Plugin.OneNote.Search public class TitleSearch(PluginInitContext context, Settings settings, ResultCreator resultCreator, RootCache rootCache) : SearchBase(context, settings, resultCreator, settings.Keywords.TitleSearch) { - public override List GetResults(Query query) => Filter(query.Search, null, rootCache.Root.Notebooks); + public override List GetResults(Query query) => GetResults(query.Search, null, rootCache.Root.Notebooks); - public List Filter(string query, IOneNoteItem? parent, IEnumerable collection) + public List GetResults(string query, IOneNoteItem? parent, IEnumerable collection) { if (query.Length == keyword.Length) return resultCreator.SearchType("Now searching by title", parent?.Name); From 568c3b520201e5def62651bda83932b42248ccb3 Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Thu, 19 Mar 2026 03:08:50 +0000 Subject: [PATCH 04/24] add sort by last modified --- .../Search/SearchExtensions.cs | 15 ++++++++++++--- .../Search/StandardSearch.cs | 2 ++ .../Search/TitleSearch.cs | 19 ++++++++++++++++++- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs b/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs index 20ac402..3d0797d 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs @@ -1,11 +1,12 @@ using System; using System.Collections.Generic; +using System.Linq; using Flow.Launcher.Plugin.SharedModels; using LinqToOneNote; namespace Flow.Launcher.Plugin.OneNote.Search { - public record struct SearchResult(T Item, List? HighlightData, int Score) where T : IOneNoteItem; + public record struct SearchResult(T Item, List? HighlightData, int Score) where T : IOneNoteItem; // (Purposeful) Mutable struct :o public static class SearchExtensions { public static IEnumerable> FuzzySearch(this IEnumerable source, string search, PluginInitContext context) where T: IOneNoteItem @@ -41,9 +42,17 @@ public static IEnumerable FilterBySettings(this IEnumerable source, Set } } - public static bool StartsWithOrd(this string str, string value) + public static IEnumerable> SortByLastModified(this IEnumerable> source, bool sortByLastModified) where T : IOneNoteItem { - return str.StartsWith(value, StringComparison.Ordinal); + return sortByLastModified + ? source.OrderByDescending(x => + { + x.Score = 0; + return x.Item.LastModified; + }) + : source; } + + public static bool StartsWithOrd(this string str, string value) => str.StartsWith(value, StringComparison.Ordinal); } } \ No newline at end of file diff --git a/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs b/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs index 6daffc3..34b51ca 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs @@ -8,7 +8,9 @@ namespace Flow.Launcher.Plugin.OneNote.Search public class StandardSearch(PluginInitContext context, Settings settings, ResultCreator resultCreator) : SearchBase(context, settings, resultCreator, Keyword.Empty) { + //Standard search public override List GetResults(Query query) => Filter(query.Search, null, keyword); + //Scope search public List GetResults(Query query, IOneNoteItem? parent) => Filter(query.Search, parent, settings.Keywords.ScopedSearch); private List Filter(string query, IOneNoteItem? parent, Keyword keyword) diff --git a/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs b/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs index 9f3ac14..d83ce86 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs @@ -16,10 +16,27 @@ public List GetResults(string query, IOneNoteItem? parent, IEnumerable resultCreator.CreateOneNoteItemResult(x.Item, false, x.HighlightData, x.Score)) + .SortByLastModified(sortByLastModified) + .Select(x => + { + Result result = resultCreator.CreateOneNoteItemResult(x.Item, false, x.HighlightData, x.Score); + result.AddSelectedCount = !sortByLastModified; + return result; + }) .ToList(); return results.Count != 0 ? results : ResultCreator.NoMatchesFound(); From aaaaec619cfa7fa351e84ffb4e16f478eef39b49 Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Thu, 26 Mar 2026 21:10:29 +0000 Subject: [PATCH 05/24] Style changes --- Flow.Launcher.Plugin.OneNote/Keywords.cs | 1 - .../Search/SearchExtensions.cs | 2 +- .../Search/SearchManager.cs | 13 ++++++------- .../Search/StandardSearch.cs | 8 ++++---- Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs | 9 +++++---- 5 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Flow.Launcher.Plugin.OneNote/Keywords.cs b/Flow.Launcher.Plugin.OneNote/Keywords.cs index bbb74b5..c8988dd 100644 --- a/Flow.Launcher.Plugin.OneNote/Keywords.cs +++ b/Flow.Launcher.Plugin.OneNote/Keywords.cs @@ -12,7 +12,6 @@ public class Keywords public Keyword RecentPages { get; set; } = new ("rp:"); public Keyword TitleSearch { get; set; } = new ("*"); public Keyword ScopedSearch { get; set; } = new (">"); - } [JsonConverter(typeof(KeywordJsonConverter))] diff --git a/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs b/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs index 3d0797d..2d54d4f 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs @@ -42,7 +42,7 @@ public static IEnumerable FilterBySettings(this IEnumerable source, Set } } - public static IEnumerable> SortByLastModified(this IEnumerable> source, bool sortByLastModified) where T : IOneNoteItem + public static IEnumerable> OrderByLastModified(this IEnumerable> source, bool sortByLastModified) where T : IOneNoteItem { return sortByLastModified ? source.OrderByDescending(x => diff --git a/Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs b/Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs index bb371e8..c968b6a 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs @@ -8,15 +8,14 @@ public class SearchManager private readonly NotebookExplorer notebookExplorer; private readonly StandardSearch standardSearch; private readonly RecentPages recentPages; - private readonly RootCache rootCache; - public RootCache RootCache => rootCache; + public RootCache RootCache { get; } public SearchManager(PluginInitContext context, Settings settings, ResultCreator resultCreator) { - rootCache = new RootCache(); - titleSearch = new TitleSearch(context, settings, resultCreator, rootCache); - notebookExplorer = new NotebookExplorer(context, settings, resultCreator, rootCache); - recentPages = new RecentPages(context, settings, resultCreator, rootCache); + RootCache = new RootCache(); + titleSearch = new TitleSearch(context, settings, resultCreator, RootCache); + notebookExplorer = new NotebookExplorer(context, settings, resultCreator, RootCache); + recentPages = new RecentPages(context, settings, resultCreator, RootCache); standardSearch = new StandardSearch(context, settings, resultCreator); } @@ -24,7 +23,7 @@ public List Query(Query query) { if (query.IsReQuery) { - rootCache.SetDirty(); + RootCache.SetDirty(); } string search = query.Search; return search switch diff --git a/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs b/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs index 34b51ca..15fcd39 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs @@ -13,15 +13,15 @@ public class StandardSearch(PluginInitContext context, Settings settings, Result //Scope search public List GetResults(Query query, IOneNoteItem? parent) => Filter(query.Search, parent, settings.Keywords.ScopedSearch); - private List Filter(string query, IOneNoteItem? parent, Keyword keyword) + private List Filter(string search, IOneNoteItem? parent, Keyword keyword) { - if (query.Length == keyword.Length) + if (search.Length == keyword.Length) return resultCreator.SearchType("Now searching all pages", parent?.Name); - if (!char.IsLetterOrDigit(query[keyword.Length])) + if (!char.IsLetterOrDigit(search[keyword.Length])) return resultCreator.InvalidQuery(); - string currentSearch = query[keyword.Length..]; + string currentSearch = search[keyword.Length..]; IEnumerable pages = parent is null ? OneNoteApp.FindPages(currentSearch) diff --git a/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs b/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs index d83ce86..79cd2a1 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs @@ -17,20 +17,21 @@ public List GetResults(string query, IOneNoteItem? parent, IEnumerable { Result result = resultCreator.CreateOneNoteItemResult(x.Item, false, x.HighlightData, x.Score); From c31287795ed6c30c59852045f8ba98cd9d9f8edc Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Fri, 27 Mar 2026 11:02:01 +0000 Subject: [PATCH 06/24] Fix incorrect sorting --- Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs b/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs index 2d54d4f..231dafc 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs @@ -6,7 +6,11 @@ namespace Flow.Launcher.Plugin.OneNote.Search { - public record struct SearchResult(T Item, List? HighlightData, int Score) where T : IOneNoteItem; // (Purposeful) Mutable struct :o + public record SearchResult(T Item, List? HighlightData, int Score) where T : IOneNoteItem + { + public int Score { get; set; } = Score; + } + public static class SearchExtensions { public static IEnumerable> FuzzySearch(this IEnumerable source, string search, PluginInitContext context) where T: IOneNoteItem From c32143041355ba636d072c056f60be17e1183a1f Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Fri, 27 Mar 2026 21:28:26 +0000 Subject: [PATCH 07/24] Allow recent pages to be searchable --- Flow.Launcher.Plugin.OneNote/Search/RecentPages.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Flow.Launcher.Plugin.OneNote/Search/RecentPages.cs b/Flow.Launcher.Plugin.OneNote/Search/RecentPages.cs index 41f7129..7b1131e 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/RecentPages.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/RecentPages.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using LinqToOneNote; +using OneNoteApp = LinqToOneNote.OneNote; namespace Flow.Launcher.Plugin.OneNote.Search { @@ -13,6 +14,16 @@ public override List GetResults(Query query) string search = query.Search; if (search.Length > keyword.Length && int.TryParse(search[keyword.Length..], out int userChosenCount)) count = userChosenCount; + + string currentSearch = query.Search[keyword.Length..]; + if (!string.IsNullOrWhiteSpace(currentSearch)) + { + return OneNoteApp.FindPages(currentSearch) + .FilterBySettings(settings) + .OrderByDescending(pg => pg.LastModified) + .Select(resultCreator.CreateRecentPageResult) + .ToList(); + } return rootCache.Root .Notebooks From f8e147cba1aae2e9e3438af0f7db6121ea543897 Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Mon, 30 Mar 2026 19:23:58 +0100 Subject: [PATCH 08/24] Move keyword check and sorting --- .../Search/SearchBase.cs | 16 +++++++++++ .../Search/SearchExtensions.cs | 9 +++--- .../Search/TitleSearch.cs | 28 ++++++------------- 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/Flow.Launcher.Plugin.OneNote/Search/SearchBase.cs b/Flow.Launcher.Plugin.OneNote/Search/SearchBase.cs index 12bc90f..9662b9c 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/SearchBase.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/SearchBase.cs @@ -10,5 +10,21 @@ public abstract class SearchBase(PluginInitContext context, Settings settings, R public readonly Keyword keyword = keyword; protected Keywords Keywords => settings.Keywords; public abstract List GetResults(Query query); + + protected bool GetSortOrder(ref string currentSearch, out bool emptySearchOnSorted) + { + emptySearchOnSorted = false; + Keyword lastModifiedKeyword = Keywords.RecentPages; + if (!currentSearch.StartsWithOrd(lastModifiedKeyword)) + return false; + if (currentSearch.Length == lastModifiedKeyword.Length) + { + emptySearchOnSorted = true; + } + currentSearch = currentSearch[lastModifiedKeyword.Length..]; + return true; + } + + } } \ No newline at end of file diff --git a/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs b/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs index 231dafc..51bf215 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs @@ -46,13 +46,14 @@ public static IEnumerable FilterBySettings(this IEnumerable source, Set } } - public static IEnumerable> OrderByLastModified(this IEnumerable> source, bool sortByLastModified) where T : IOneNoteItem + public static IEnumerable OrderByLastModified(this IEnumerable source, bool sortByLastModified) { return sortByLastModified - ? source.OrderByDescending(x => + ? source.OrderByDescending(result => { - x.Score = 0; - return x.Item.LastModified; + result.Score = 0; + result.AddSelectedCount = !sortByLastModified; + return ((IOneNoteItem)result.ContextData).LastModified; }) : source; } diff --git a/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs b/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs index 79cd2a1..e287166 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs @@ -8,36 +8,26 @@ public class TitleSearch(PluginInitContext context, Settings settings, ResultCre : SearchBase(context, settings, resultCreator, settings.Keywords.TitleSearch) { public override List GetResults(Query query) => GetResults(query.Search, null, rootCache.Root.Notebooks); - + public List GetResults(string query, IOneNoteItem? parent, IEnumerable collection) { + const string emptySearchPrefix = "Now searching by title"; if (query.Length == keyword.Length) - return resultCreator.SearchType("Now searching by title", parent?.Name); + //TODO return Searching by title and a Searching by last modified result with an action to auto complete last modified. + return resultCreator.SearchType(emptySearchPrefix, parent?.Name); var currentSearch = query[keyword.Length..]; - bool sortByLastModified = false; - var lastModifiedKeyword = Keywords.RecentPages; - if (currentSearch.StartsWithOrd(lastModifiedKeyword)) - { - if (currentSearch.Length == lastModifiedKeyword.Length) - { - return resultCreator.SearchType("Now searching by title ordered by last modified", parent?.Name); - } - currentSearch = currentSearch[lastModifiedKeyword.Length..]; - sortByLastModified = true; - } + var sortByLastModified = GetSortOrder(ref currentSearch, out var emptySearch); + + if(emptySearch) + return resultCreator.SearchType($"{emptySearchPrefix} ordered by last modified", parent?.Name);; var results = collection.Descendants() .FilterBySettings(settings) .FuzzySearch(currentSearch, context) + .Select(x => resultCreator.CreateOneNoteItemResult(x.Item, false, x.HighlightData, x.Score)) .OrderByLastModified(sortByLastModified) - .Select(x => - { - Result result = resultCreator.CreateOneNoteItemResult(x.Item, false, x.HighlightData, x.Score); - result.AddSelectedCount = !sortByLastModified; - return result; - }) .ToList(); return results.Count != 0 ? results : ResultCreator.NoMatchesFound(); From 26bcead4296f3a1884ac72fc0b3e928d8a991cdb Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Sat, 11 Apr 2026 16:13:59 +0100 Subject: [PATCH 09/24] wip: refactor Search manager again --- Flow.Launcher.Plugin.OneNote/ResultCreator.cs | 15 +- .../Search/Rework/SearchManager.cs | 275 ++++++++++++++++++ 2 files changed, 288 insertions(+), 2 deletions(-) create mode 100644 Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs diff --git a/Flow.Launcher.Plugin.OneNote/ResultCreator.cs b/Flow.Launcher.Plugin.OneNote/ResultCreator.cs index bb954ea..fa2c58d 100644 --- a/Flow.Launcher.Plugin.OneNote/ResultCreator.cs +++ b/Flow.Launcher.Plugin.OneNote/ResultCreator.cs @@ -224,7 +224,7 @@ await Task.Run(() => public Result CreatePageResult(Page page, string query) => CreateOneNoteItemResult(page, false, string.IsNullOrWhiteSpace(query) ? null : context.API.FuzzySearch(query, page.Name).MatchData); - public Result CreateRecentPageResult(Page page) + public Result CreateRecentPageResult(Page page) //TODO: make it accept any IOneNoteItem type { var result = CreateOneNoteItemResult(page, false); result.SubTitle = $"{page.LastModified.Humanize()} | {result.SubTitle}"; @@ -232,6 +232,17 @@ public Result CreateRecentPageResult(Page page) result.AddSelectedCount = false; return result; } + + + public Result CreateRecentItemResult(IOneNoteItem item, List? highlightData = null, int score = 0) + { + var result = CreateOneNoteItemResult(item, false, highlightData, score); + result.SubTitle = $"{item.LastModified.Humanize()} | {result.SubTitle}"; + if(item is Page) + result.IcoPath = iconProvider.Recent; + result.AddSelectedCount = false; + return result; + } //When name can have invalid chars private Result CreateNewItemResult(string newName, TParent? parent, string iconPath, Func createFunc) @@ -406,7 +417,7 @@ public List SearchType(string title, string? parentName, string subTitle return SingleResult(title, subTitle, iconProvider.Search); } - private static List SingleResult(string title, string subTitle, string iconPath) + public static List SingleResult(string title, string subTitle, string iconPath) { return new List { diff --git a/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs b/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs new file mode 100644 index 0000000..4e902b3 --- /dev/null +++ b/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs @@ -0,0 +1,275 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using LinqToOneNote; +using LinqToOneNote.Abstractions; +using OneNoteApp = LinqToOneNote.OneNote; + +namespace Flow.Launcher.Plugin.OneNote.Search.Rework +{ + //TODO: sort and switch + // for sort pass in through SearchData + public class SearchManager + { + private readonly OrderedDictionary searchFunctions; //TODO: mutable keys beware/ need to fix + private readonly Keywords keywords; + private readonly PluginInitContext context; + public RootCache RootCache { get; } = new RootCache(); + + public SearchManager(PluginInitContext context, Settings settings, ResultCreator resultCreator) + { + keywords = settings.Keywords; + this.context = context; + searchFunctions = new OrderedDictionary() + { + { keywords.NotebookExplorer, new NotebookExplorer() }, + { keywords.TitleSearch, new TitleSearch() }, + { keywords.ScopedSearch, new StandardSearch() }, + { Keyword.Empty, new StandardSearch() }, + }; + } + + private bool TryParseKeywords(string search, out (Keyword keyword, SearchData searchData) data) + { + foreach (Keyword keyword in searchFunctions.Keys) + { + if (TryParse(keyword, out var searchData)) + { + data = (keyword, searchData.Value); + return true; + } + } + + //Should never fail; + //context.API.LogWarn(nameof(SearchManager), $"Could not parse keywords from search: {search}. This should never happen..."); + data = default; + return false; + + bool TryParse(Keyword keyword, [NotNullWhen(true)] out SearchData? searchData) //TODO: Use span for performance + { + if (!search.StartsWithOrd(keyword)) + { + searchData = null; + return false; + } + + if ( /*keyword != keywords.NotebookExplorer &&*/ search[keyword.Length..].StartsWith(keywords.RecentPages)) //TODO maybe allow NotebookExplorer+ RecentPAges? + { + searchData = new SearchData( + search[(keyword.Length + keywords.RecentPages.Length)..], + null, + RootCache.Root.Notebooks, + SortOrder.RecentlyModified + ); + return true; + } + + searchData = new SearchData(search[keyword.Length..], null, RootCache.Root.Notebooks, SortOrder.Default); + return true; + } + } + + public List Query(Query query) + { + if (query.IsReQuery) + { + RootCache.SetDirty(); + } + + if (!TryParseKeywords(query.Search, out var data)) + return ResultCreator.SingleResult("Failed at parsing keywords", string.Empty, string.Empty); + + var (keyword, searchData) = data; + var searchFunction = searchFunctions[keyword]; + + var results = string.IsNullOrWhiteSpace(searchData.search) + ? searchFunction.EmptySearch(in searchData) + : searchFunction.GetResults(in searchData); + + return results.Count != 0 ? results : ResultCreator.NoMatchesFound(); + } + } + + public abstract class SearchFunction + { + protected ResultCreator resultCreator; + protected Settings settings; + protected PluginInitContext context; + public abstract List GetResults(in SearchData searchData); + public abstract List EmptySearch(in SearchData searchData); + } + + public class StandardSearch : SearchFunction + { + public override List GetResults(in SearchData searchData) + { + var (search, parent, _, sortOrder) = searchData; + + if (!char.IsLetterOrDigit(search[0])) + return resultCreator.InvalidQuery(); + + IEnumerable pages = parent is null + ? OneNoteApp.FindPages(search) + : OneNoteApp.FindPages(search, parent); + + return sortOrder switch + { + SortOrder.RecentlyModified => pages.OrderByDescending(pg => pg.LastModified) + .Select(pg => resultCreator.CreateRecentItemResult(pg)) + .ToList(), + _ => pages.Select(pg => resultCreator.CreatePageResult(pg, search)).ToList() + }; + } + + public override List EmptySearch(in SearchData searchData) => resultCreator.SearchType("Now searching all pages", searchData.parent?.Name); + } + + public class TitleSearch : SearchFunction + { + public override List GetResults(in SearchData searchData) + { + var (search, _, collection, sortOrder) = searchData; + var results = collection.Descendants() + .FilterBySettings(settings) + .FuzzySearch(search, context); + + return sortOrder switch + { + SortOrder.RecentlyModified => results.OrderByDescending(sr => sr.Item.LastModified) + .Select(x => resultCreator.CreateRecentItemResult(x.Item, x.HighlightData, x.Score)) + .ToList(), + _ => results.Select(x => resultCreator.CreateOneNoteItemResult(x.Item, false, x.HighlightData, x.Score)) + .ToList(), + }; + } + + public override List EmptySearch(in SearchData searchData) + { + const string emptySearchPrefix = "Now searching by title"; + return searchData.sortOrder switch + { + SortOrder.RecentlyModified => resultCreator.SearchType($"{emptySearchPrefix} ordered by last modified", searchData.parent?.Name), + _ => resultCreator.SearchType(emptySearchPrefix, searchData.parent?.Name), + }; + } + } + + public class NotebookExplorer : SearchFunction + { + private Explorer explorer; + + public override List GetResults(in SearchData searchData) + { + //TODO: check if searchData.search is empty and searchData.sortOrder is recently modified + if (ValidateSearch(in searchData, out SearchData data)) + return resultCreator.InvalidQuery(false); + + + // if (parent == null) + // return results; + // + // Result result = resultCreator.CreateOneNoteItemResult(parent, false, score: Result.MaxScore); + // result.Title = $"Open \"{parent.Name}\" in OneNote"; + // result.SubTitle = $"Use \'{Keywords.ScopedSearch}\' to search this item. Use \'{Keywords.TitleSearch}\' to search by title in this item"; + // results.Add(result); + // return results; + } + + private bool ValidateSearch(in SearchData inData, out SearchData searchData) + { + (var search, IOneNoteItem? parent, IEnumerable? collection, _) = inData; + Debug.Assert(collection != null, nameof(collection) + " != null"); + //parent is null here + //collection is rootCache.Root.Notebooks as this functions validates the search with the whole query; + + string lastSearch = string.Empty; + + const string separator = Keywords.NotebookExplorerSeparator; + var currIndex = search.IndexOf(separator, StringComparison.Ordinal); + var prevIndex = 0; + + while (currIndex != -1) + { + var itemName = search[prevIndex..currIndex]; + parent = collection.FirstOrDefault(item => item.Name == itemName); + if (parent == null) + { + searchData = default; + return false; + } + + collection = parent.Children; + + prevIndex = currIndex + 1; + currIndex = search.IndexOf(separator, currIndex + separator.Length, StringComparison.Ordinal); + } + + lastSearch = search[prevIndex..]; + searchData = inData with { search = lastSearch, parent = parent, collection = collection }; + return true; + } + + public override List EmptySearch(in SearchData searchData) => explorer.EmptySearch(in searchData); + + + private class Explorer : SearchFunction + { + public override List GetResults(in SearchData searchData) + { + var (search, parent, collection, sortOrder) = searchData; //TODO: sort order + + var results = collection.FilterBySettings(settings) + .FuzzySearch(search, context) + .Select(r => resultCreator.CreateOneNoteItemResult(r.Item, true, r.HighlightData, r.Score)) + .ToList(); + + // If parent is a section, pages inside can have the same name + if (parent is not Section && results.Any(result => string.Equals(search.Trim(), result.Title, StringComparison.OrdinalIgnoreCase))) + return results; + + if (parent?.IsInRecycleBin() == true) + return results; + + //Add option to create new items + switch (parent) + { + case null: + results.Add(resultCreator.CreateNewNotebookResult(search)); + break; + case INotebookOrSectionGroup x: + results.Add(resultCreator.CreateNewSectionResult(search, x)); + results.Add(resultCreator.CreateNewSectionGroupResult(search, x)); + break; + case Section section: + if (!section.Locked) + { + results.Add(resultCreator.CreateNewPageResult(search, section)); + } + break; + } + + return results; + } + + public override List EmptySearch(in SearchData searchData) + { + Debug.Assert(searchData.collection != null); + var results = searchData.collection.FilterBySettings(settings) + .Select(item => resultCreator.CreateOneNoteItemResult(item, true)) + .ToList(); + + return results.Count != 0 ? results : resultCreator.EmptyCollection(results, searchData.parent); + } + } + } + + public readonly record struct SearchData(string search, IOneNoteItem? parent, IEnumerable? collection, SortOrder sortOrder); + + public enum SortOrder + { + Default = 0, + RecentlyModified = 1, + } +} \ No newline at end of file From 5f486deda829f90a3aa254661eaa0f97b62c14ce Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Sat, 11 Apr 2026 17:02:12 +0100 Subject: [PATCH 10/24] wip: refactor make SearchData mutable --- .../Search/Rework/SearchManager.cs | 80 +++++++++---------- 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs b/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs index 4e902b3..3831ffa 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs @@ -22,51 +22,50 @@ public SearchManager(PluginInitContext context, Settings settings, ResultCreator { keywords = settings.Keywords; this.context = context; + var standardSearch = new StandardSearch(); searchFunctions = new OrderedDictionary() { { keywords.NotebookExplorer, new NotebookExplorer() }, { keywords.TitleSearch, new TitleSearch() }, - { keywords.ScopedSearch, new StandardSearch() }, - { Keyword.Empty, new StandardSearch() }, + { keywords.ScopedSearch, standardSearch }, + { Keyword.Empty, standardSearch }, }; } - private bool TryParseKeywords(string search, out (Keyword keyword, SearchData searchData) data) + //Get the keyword + private static bool TryParseSearch(ref SearchData searchData, [NotNullWhen(true)] out Keyword? keyword, IEnumerable keywordsToMatch, Keywords keywords) { - foreach (Keyword keyword in searchFunctions.Keys) + foreach (Keyword key in keywordsToMatch) { - if (TryParse(keyword, out var searchData)) + if (TryParse(key, ref searchData, keywords)) { - data = (keyword, searchData.Value); + keyword = key; return true; } } //Should never fail; //context.API.LogWarn(nameof(SearchManager), $"Could not parse keywords from search: {search}. This should never happen..."); - data = default; + keyword = null; return false; - bool TryParse(Keyword keyword, [NotNullWhen(true)] out SearchData? searchData) //TODO: Use span for performance + static bool TryParse(Keyword keyword, ref SearchData searchData, Keywords keywords) //TODO: Use span for performance { + (var search, _,_,_) = searchData; if (!search.StartsWithOrd(keyword)) { - searchData = null; return false; } if ( /*keyword != keywords.NotebookExplorer &&*/ search[keyword.Length..].StartsWith(keywords.RecentPages)) //TODO maybe allow NotebookExplorer+ RecentPAges? { - searchData = new SearchData( - search[(keyword.Length + keywords.RecentPages.Length)..], - null, - RootCache.Root.Notebooks, - SortOrder.RecentlyModified - ); + searchData.search = search[(keyword.Length + keywords.RecentPages.Length)..]; + searchData.sortOrder = SortOrder.RecentlyModified; return true; } - searchData = new SearchData(search[keyword.Length..], null, RootCache.Root.Notebooks, SortOrder.Default); + searchData.search = search[keyword.Length..]; + searchData.sortOrder = SortOrder.Default; return true; } } @@ -78,15 +77,15 @@ public List Query(Query query) RootCache.SetDirty(); } - if (!TryParseKeywords(query.Search, out var data)) + var searchData = new SearchData(query.Search, null, RootCache.Root.Notebooks, SortOrder.Default); + if (!TryParseSearch(ref searchData, out var keyword, searchFunctions.Keys, keywords)) return ResultCreator.SingleResult("Failed at parsing keywords", string.Empty, string.Empty); - var (keyword, searchData) = data; var searchFunction = searchFunctions[keyword]; var results = string.IsNullOrWhiteSpace(searchData.search) - ? searchFunction.EmptySearch(in searchData) - : searchFunction.GetResults(in searchData); + ? searchFunction.EmptySearch(ref searchData) + : searchFunction.GetResults(ref searchData); return results.Count != 0 ? results : ResultCreator.NoMatchesFound(); } @@ -97,13 +96,13 @@ public abstract class SearchFunction protected ResultCreator resultCreator; protected Settings settings; protected PluginInitContext context; - public abstract List GetResults(in SearchData searchData); - public abstract List EmptySearch(in SearchData searchData); + public abstract List GetResults(ref SearchData searchData); + public abstract List EmptySearch(ref SearchData searchData); } public class StandardSearch : SearchFunction { - public override List GetResults(in SearchData searchData) + public override List GetResults(ref SearchData searchData) { var (search, parent, _, sortOrder) = searchData; @@ -123,12 +122,12 @@ public override List GetResults(in SearchData searchData) }; } - public override List EmptySearch(in SearchData searchData) => resultCreator.SearchType("Now searching all pages", searchData.parent?.Name); + public override List EmptySearch(ref SearchData searchData) => resultCreator.SearchType("Now searching all pages", searchData.parent?.Name); } public class TitleSearch : SearchFunction { - public override List GetResults(in SearchData searchData) + public override List GetResults(ref SearchData searchData) { var (search, _, collection, sortOrder) = searchData; var results = collection.Descendants() @@ -145,7 +144,7 @@ public override List GetResults(in SearchData searchData) }; } - public override List EmptySearch(in SearchData searchData) + public override List EmptySearch(ref SearchData searchData) { const string emptySearchPrefix = "Now searching by title"; return searchData.sortOrder switch @@ -158,15 +157,15 @@ public override List EmptySearch(in SearchData searchData) public class NotebookExplorer : SearchFunction { - private Explorer explorer; + private Explorer explorer = new Explorer(); - public override List GetResults(in SearchData searchData) + public override List GetResults(ref SearchData searchData) { //TODO: check if searchData.search is empty and searchData.sortOrder is recently modified - if (ValidateSearch(in searchData, out SearchData data)) + if (!ValidateSearch(ref searchData)) return resultCreator.InvalidQuery(false); - - + + return null; // if (parent == null) // return results; // @@ -177,9 +176,9 @@ public override List GetResults(in SearchData searchData) // return results; } - private bool ValidateSearch(in SearchData inData, out SearchData searchData) + private bool ValidateSearch(ref SearchData searchData) { - (var search, IOneNoteItem? parent, IEnumerable? collection, _) = inData; + (var search, IOneNoteItem? parent, IEnumerable? collection, _) = searchData; Debug.Assert(collection != null, nameof(collection) + " != null"); //parent is null here //collection is rootCache.Root.Notebooks as this functions validates the search with the whole query; @@ -195,10 +194,7 @@ private bool ValidateSearch(in SearchData inData, out SearchData searchData) var itemName = search[prevIndex..currIndex]; parent = collection.FirstOrDefault(item => item.Name == itemName); if (parent == null) - { - searchData = default; return false; - } collection = parent.Children; @@ -207,16 +203,18 @@ private bool ValidateSearch(in SearchData inData, out SearchData searchData) } lastSearch = search[prevIndex..]; - searchData = inData with { search = lastSearch, parent = parent, collection = collection }; + searchData.search = lastSearch; + searchData.parent = parent; + searchData.collection = collection; return true; } - public override List EmptySearch(in SearchData searchData) => explorer.EmptySearch(in searchData); + public override List EmptySearch(ref SearchData searchData) => explorer.EmptySearch(ref searchData); private class Explorer : SearchFunction { - public override List GetResults(in SearchData searchData) + public override List GetResults(ref SearchData searchData) { var (search, parent, collection, sortOrder) = searchData; //TODO: sort order @@ -253,7 +251,7 @@ public override List GetResults(in SearchData searchData) return results; } - public override List EmptySearch(in SearchData searchData) + public override List EmptySearch(ref SearchData searchData) { Debug.Assert(searchData.collection != null); var results = searchData.collection.FilterBySettings(settings) @@ -265,7 +263,7 @@ public override List EmptySearch(in SearchData searchData) } } - public readonly record struct SearchData(string search, IOneNoteItem? parent, IEnumerable? collection, SortOrder sortOrder); + public record struct SearchData(string search, IOneNoteItem? parent, IEnumerable? collection, SortOrder sortOrder); public enum SortOrder { From f47bab815106e43b5e50ec9a0a65421843f68f7c Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Sat, 11 Apr 2026 17:49:35 +0100 Subject: [PATCH 11/24] wip: finish search functions --- .../Search/Rework/SearchManager.cs | 111 +++++++++++------- 1 file changed, 70 insertions(+), 41 deletions(-) diff --git a/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs b/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs index 3831ffa..a452d98 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs @@ -33,41 +33,32 @@ public SearchManager(PluginInitContext context, Settings settings, ResultCreator } //Get the keyword - private static bool TryParseSearch(ref SearchData searchData, [NotNullWhen(true)] out Keyword? keyword, IEnumerable keywordsToMatch, Keywords keywords) + public static bool TryParseSearch(ref SearchData searchData, [NotNullWhen(true)] out Keyword? keyword, IList keywordsToMatch, Keywords keywords) { - foreach (Keyword key in keywordsToMatch) + var search = searchData.search; + for (var i = 0; i < keywordsToMatch.Count; i++) { - if (TryParse(key, ref searchData, keywords)) - { - keyword = key; - return true; - } - } + Keyword key = keywordsToMatch[i]; + if (!search.StartsWithOrd(key)) + continue; - //Should never fail; - //context.API.LogWarn(nameof(SearchManager), $"Could not parse keywords from search: {search}. This should never happen..."); - keyword = null; - return false; - - static bool TryParse(Keyword keyword, ref SearchData searchData, Keywords keywords) //TODO: Use span for performance - { - (var search, _,_,_) = searchData; - if (!search.StartsWithOrd(keyword)) + keyword = key; + if ( /*keyword != keywords.NotebookExplorer &&*/ search[key.Length..].StartsWith(keywords.RecentPages)) //TODO maybe allow NotebookExplorer+ RecentPAges? { - return false; - } - - if ( /*keyword != keywords.NotebookExplorer &&*/ search[keyword.Length..].StartsWith(keywords.RecentPages)) //TODO maybe allow NotebookExplorer+ RecentPAges? - { - searchData.search = search[(keyword.Length + keywords.RecentPages.Length)..]; + searchData.search = search[(key.Length + keywords.RecentPages.Length)..]; searchData.sortOrder = SortOrder.RecentlyModified; return true; } - searchData.search = search[keyword.Length..]; + searchData.search = search[key.Length..]; searchData.sortOrder = SortOrder.Default; return true; } + + //Should never fail; + //context.API.LogWarn(nameof(SearchManager), $"Could not parse keywords from search: {search}. This should never happen..."); + keyword = null; + return false; } public List Query(Query query) @@ -122,7 +113,19 @@ public override List GetResults(ref SearchData searchData) }; } - public override List EmptySearch(ref SearchData searchData) => resultCreator.SearchType("Now searching all pages", searchData.parent?.Name); + public override List EmptySearch(ref SearchData searchData) + { + return searchData.sortOrder switch + { + SortOrder.RecentlyModified => searchData.collection + .GetAllPages() + .FilterBySettings(settings) + .OrderByDescending(pg => pg.LastModified) + .Select(pg => resultCreator.CreateRecentItemResult(pg)) + .ToList(), + _ => resultCreator.SearchType("Now searching all pages", searchData.parent?.Name), + } + } } public class TitleSearch : SearchFunction @@ -137,9 +140,9 @@ public override List GetResults(ref SearchData searchData) return sortOrder switch { SortOrder.RecentlyModified => results.OrderByDescending(sr => sr.Item.LastModified) - .Select(x => resultCreator.CreateRecentItemResult(x.Item, x.HighlightData, x.Score)) + .Select(sr => resultCreator.CreateRecentItemResult(sr.Item, sr.HighlightData)) .ToList(), - _ => results.Select(x => resultCreator.CreateOneNoteItemResult(x.Item, false, x.HighlightData, x.Score)) + _ => results.Select(sr => resultCreator.CreateOneNoteItemResult(sr.Item, false, sr.HighlightData, sr.Score)) .ToList(), }; } @@ -158,6 +161,15 @@ public override List EmptySearch(ref SearchData searchData) public class NotebookExplorer : SearchFunction { private Explorer explorer = new Explorer(); + private Keywords keywords; + private Keyword[] keywordsToMatch; + private TitleSearch titleSearch; + private StandardSearch standardSearch; + + public NotebookExplorer() + { + keywordsToMatch = [keywords.TitleSearch, keywords.ScopedSearch, Keyword.Empty]; + } public override List GetResults(ref SearchData searchData) { @@ -165,15 +177,25 @@ public override List GetResults(ref SearchData searchData) if (!ValidateSearch(ref searchData)) return resultCreator.InvalidQuery(false); - return null; - // if (parent == null) - // return results; - // - // Result result = resultCreator.CreateOneNoteItemResult(parent, false, score: Result.MaxScore); - // result.Title = $"Open \"{parent.Name}\" in OneNote"; - // result.SubTitle = $"Use \'{Keywords.ScopedSearch}\' to search this item. Use \'{Keywords.TitleSearch}\' to search by title in this item"; - // results.Add(result); - // return results; + if(!SearchManager.TryParseSearch(ref searchData, out Keyword? keyword, keywordsToMatch, keywords)) + return ResultCreator.SingleResult("Failed at parsing keywords", string.Empty, string.Empty); + + IOneNoteItem? parent = searchData.parent; + var results = (keyword, parent) switch + { + (not null, not Page) when keyword == keywords.TitleSearch => titleSearch.GetResults(ref searchData), + (not null, INotebookOrSectionGroup) when keyword == keywords.ScopedSearch => standardSearch.GetResults(ref searchData), + _ => explorer.GetResults(ref searchData), + }; + + if (parent == null) + return results; + + Result result = resultCreator.CreateOneNoteItemResult(parent, false, score: Result.MaxScore); + result.Title = $"Open \"{parent.Name}\" in OneNote"; + result.SubTitle = $"Use \'{keywords.ScopedSearch}\' to search this item. Use \'{keywords.TitleSearch}\' to search by title in this item. Use \'{keywords.RecentPages}\' to sort by recently modified"; + results.Add(result); + return results; } private bool ValidateSearch(ref SearchData searchData) @@ -217,11 +239,18 @@ private class Explorer : SearchFunction public override List GetResults(ref SearchData searchData) { var (search, parent, collection, sortOrder) = searchData; //TODO: sort order - - var results = collection.FilterBySettings(settings) - .FuzzySearch(search, context) - .Select(r => resultCreator.CreateOneNoteItemResult(r.Item, true, r.HighlightData, r.Score)) - .ToList(); + + Debug.Assert(collection != null, nameof(collection) + " != null"); + var searchResults = collection.FilterBySettings(settings) + .FuzzySearch(search, context); + var results = sortOrder switch + { + SortOrder.RecentlyModified => searchResults.OrderByDescending(sr => sr.Item.LastModified) + .Select(sr => resultCreator.CreateRecentItemResult(sr.Item, sr.HighlightData)) + .ToList(), + _ => searchResults.Select(r => resultCreator.CreateOneNoteItemResult(r.Item, true, r.HighlightData, r.Score)) + .ToList(), + }; // If parent is a section, pages inside can have the same name if (parent is not Section && results.Any(result => string.Equals(search.Trim(), result.Title, StringComparison.OrdinalIgnoreCase))) From 17f19347a1b4e3b2526c4e28ff8ce4160eb4b8e6 Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Sat, 11 Apr 2026 18:06:58 +0100 Subject: [PATCH 12/24] Fixes to the refactor --- Flow.Launcher.Plugin.OneNote/ResultCreator.cs | 5 +- .../Search/Rework/SearchManager.cs | 94 ++++++++----------- 2 files changed, 43 insertions(+), 56 deletions(-) diff --git a/Flow.Launcher.Plugin.OneNote/ResultCreator.cs b/Flow.Launcher.Plugin.OneNote/ResultCreator.cs index fa2c58d..d054228 100644 --- a/Flow.Launcher.Plugin.OneNote/ResultCreator.cs +++ b/Flow.Launcher.Plugin.OneNote/ResultCreator.cs @@ -233,10 +233,9 @@ public Result CreateRecentPageResult(Page page) //TODO: make it accept any IOneN return result; } - - public Result CreateRecentItemResult(IOneNoteItem item, List? highlightData = null, int score = 0) + public Result CreateRecentItemResult(IOneNoteItem item, List? highlightData = null) { - var result = CreateOneNoteItemResult(item, false, highlightData, score); + var result = CreateOneNoteItemResult(item, false, highlightData); result.SubTitle = $"{item.LastModified.Humanize()} | {result.SubTitle}"; if(item is Page) result.IcoPath = iconProvider.Recent; diff --git a/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs b/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs index a452d98..f2010e2 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs @@ -9,25 +9,21 @@ namespace Flow.Launcher.Plugin.OneNote.Search.Rework { - //TODO: sort and switch - // for sort pass in through SearchData public class SearchManager { private readonly OrderedDictionary searchFunctions; //TODO: mutable keys beware/ need to fix private readonly Keywords keywords; - private readonly PluginInitContext context; public RootCache RootCache { get; } = new RootCache(); public SearchManager(PluginInitContext context, Settings settings, ResultCreator resultCreator) { keywords = settings.Keywords; - this.context = context; - var standardSearch = new StandardSearch(); + var standardSearch = new StandardSearch(context, settings, resultCreator); + var titleSearch = new TitleSearch(context, settings, resultCreator); searchFunctions = new OrderedDictionary() { - { keywords.NotebookExplorer, new NotebookExplorer() }, - { keywords.TitleSearch, new TitleSearch() }, - { keywords.ScopedSearch, standardSearch }, + { keywords.NotebookExplorer, new NotebookExplorer(context, settings, resultCreator, titleSearch, standardSearch) }, + { keywords.TitleSearch, titleSearch }, { Keyword.Empty, standardSearch }, }; } @@ -35,7 +31,7 @@ public SearchManager(PluginInitContext context, Settings settings, ResultCreator //Get the keyword public static bool TryParseSearch(ref SearchData searchData, [NotNullWhen(true)] out Keyword? keyword, IList keywordsToMatch, Keywords keywords) { - var search = searchData.search; + var search = searchData.Search; for (var i = 0; i < keywordsToMatch.Count; i++) { Keyword key = keywordsToMatch[i]; @@ -45,13 +41,13 @@ public static bool TryParseSearch(ref SearchData searchData, [NotNullWhen(true)] keyword = key; if ( /*keyword != keywords.NotebookExplorer &&*/ search[key.Length..].StartsWith(keywords.RecentPages)) //TODO maybe allow NotebookExplorer+ RecentPAges? { - searchData.search = search[(key.Length + keywords.RecentPages.Length)..]; - searchData.sortOrder = SortOrder.RecentlyModified; + searchData.Search = search[(key.Length + keywords.RecentPages.Length)..]; + searchData.SortOrder = SortOrder.RecentlyModified; return true; } - searchData.search = search[key.Length..]; - searchData.sortOrder = SortOrder.Default; + searchData.Search = search[key.Length..]; + searchData.SortOrder = SortOrder.Default; return true; } @@ -74,7 +70,7 @@ public List Query(Query query) var searchFunction = searchFunctions[keyword]; - var results = string.IsNullOrWhiteSpace(searchData.search) + var results = string.IsNullOrWhiteSpace(searchData.Search) ? searchFunction.EmptySearch(ref searchData) : searchFunction.GetResults(ref searchData); @@ -82,16 +78,16 @@ public List Query(Query query) } } - public abstract class SearchFunction + public abstract class SearchFunction(PluginInitContext context, Settings settings, ResultCreator resultCreator) { - protected ResultCreator resultCreator; - protected Settings settings; - protected PluginInitContext context; + protected readonly ResultCreator resultCreator = resultCreator; + protected readonly Settings settings = settings; + protected readonly PluginInitContext context = context; public abstract List GetResults(ref SearchData searchData); public abstract List EmptySearch(ref SearchData searchData); } - public class StandardSearch : SearchFunction + public class StandardSearch(PluginInitContext context, Settings settings, ResultCreator resultCreator) : SearchFunction(context, settings, resultCreator) { public override List GetResults(ref SearchData searchData) { @@ -115,20 +111,20 @@ public override List GetResults(ref SearchData searchData) public override List EmptySearch(ref SearchData searchData) { - return searchData.sortOrder switch + return searchData.SortOrder switch { - SortOrder.RecentlyModified => searchData.collection + SortOrder.RecentlyModified => searchData.Collection .GetAllPages() .FilterBySettings(settings) .OrderByDescending(pg => pg.LastModified) .Select(pg => resultCreator.CreateRecentItemResult(pg)) .ToList(), - _ => resultCreator.SearchType("Now searching all pages", searchData.parent?.Name), - } + _ => resultCreator.SearchType("Now searching all pages", searchData.Parent?.Name), + }; } } - public class TitleSearch : SearchFunction + public class TitleSearch(PluginInitContext context, Settings settings, ResultCreator resultCreator) : SearchFunction(context, settings, resultCreator) { public override List GetResults(ref SearchData searchData) { @@ -150,37 +146,32 @@ public override List GetResults(ref SearchData searchData) public override List EmptySearch(ref SearchData searchData) { const string emptySearchPrefix = "Now searching by title"; - return searchData.sortOrder switch + return searchData.SortOrder switch { - SortOrder.RecentlyModified => resultCreator.SearchType($"{emptySearchPrefix} ordered by last modified", searchData.parent?.Name), - _ => resultCreator.SearchType(emptySearchPrefix, searchData.parent?.Name), + SortOrder.RecentlyModified => resultCreator.SearchType($"{emptySearchPrefix} ordered by last modified", searchData.Parent?.Name), + _ => resultCreator.SearchType(emptySearchPrefix, searchData.Parent?.Name), }; } } - public class NotebookExplorer : SearchFunction + public class NotebookExplorer(PluginInitContext context, Settings settings, ResultCreator resultCreator, TitleSearch titleSearch, StandardSearch standardSearch) + : SearchFunction(context, settings, resultCreator) { - private Explorer explorer = new Explorer(); - private Keywords keywords; - private Keyword[] keywordsToMatch; - private TitleSearch titleSearch; - private StandardSearch standardSearch; - - public NotebookExplorer() - { - keywordsToMatch = [keywords.TitleSearch, keywords.ScopedSearch, Keyword.Empty]; - } + private readonly Explorer explorer = new(context, settings, resultCreator); + private readonly Keywords keywords = settings.Keywords; + private readonly Keyword[] keywordsToMatch = [settings.Keywords.TitleSearch, settings.Keywords.ScopedSearch, Keyword.Empty]; public override List GetResults(ref SearchData searchData) { - //TODO: check if searchData.search is empty and searchData.sortOrder is recently modified + Debug.Assert(!string.IsNullOrWhiteSpace(searchData.Search), "Uh oh spaghetti oh not supposed to have a value yoooou. \"string.IsNullOrWhiteSpace(searchData.Search)\""); + //TODO: check if searchData.search is empty because it should never be, it would got to EmptySearch if (!ValidateSearch(ref searchData)) return resultCreator.InvalidQuery(false); if(!SearchManager.TryParseSearch(ref searchData, out Keyword? keyword, keywordsToMatch, keywords)) return ResultCreator.SingleResult("Failed at parsing keywords", string.Empty, string.Empty); - IOneNoteItem? parent = searchData.parent; + IOneNoteItem? parent = searchData.Parent; var results = (keyword, parent) switch { (not null, not Page) when keyword == keywords.TitleSearch => titleSearch.GetResults(ref searchData), @@ -204,9 +195,7 @@ private bool ValidateSearch(ref SearchData searchData) Debug.Assert(collection != null, nameof(collection) + " != null"); //parent is null here //collection is rootCache.Root.Notebooks as this functions validates the search with the whole query; - - string lastSearch = string.Empty; - + const string separator = Keywords.NotebookExplorerSeparator; var currIndex = search.IndexOf(separator, StringComparison.Ordinal); var prevIndex = 0; @@ -224,21 +213,20 @@ private bool ValidateSearch(ref SearchData searchData) currIndex = search.IndexOf(separator, currIndex + separator.Length, StringComparison.Ordinal); } - lastSearch = search[prevIndex..]; - searchData.search = lastSearch; - searchData.parent = parent; - searchData.collection = collection; + searchData.Search = search[prevIndex..]; + searchData.Parent = parent; + searchData.Collection = collection; return true; } public override List EmptySearch(ref SearchData searchData) => explorer.EmptySearch(ref searchData); - private class Explorer : SearchFunction + private class Explorer(PluginInitContext context, Settings settings, ResultCreator resultCreator) : SearchFunction(context, settings, resultCreator) { public override List GetResults(ref SearchData searchData) { - var (search, parent, collection, sortOrder) = searchData; //TODO: sort order + var (search, parent, collection, sortOrder) = searchData; Debug.Assert(collection != null, nameof(collection) + " != null"); var searchResults = collection.FilterBySettings(settings) @@ -282,17 +270,17 @@ public override List GetResults(ref SearchData searchData) public override List EmptySearch(ref SearchData searchData) { - Debug.Assert(searchData.collection != null); - var results = searchData.collection.FilterBySettings(settings) + Debug.Assert(searchData.Collection != null); + var results = searchData.Collection.FilterBySettings(settings) .Select(item => resultCreator.CreateOneNoteItemResult(item, true)) .ToList(); - return results.Count != 0 ? results : resultCreator.EmptyCollection(results, searchData.parent); + return results.Count != 0 ? results : resultCreator.EmptyCollection(results, searchData.Parent); } } } - public record struct SearchData(string search, IOneNoteItem? parent, IEnumerable? collection, SortOrder sortOrder); + public record struct SearchData(string Search, IOneNoteItem? Parent, IEnumerable? Collection, SortOrder SortOrder); public enum SortOrder { From 078bb617a3f39bf2b50f795228937917839e4327 Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Sat, 11 Apr 2026 20:51:41 +0100 Subject: [PATCH 13/24] fix incorrect results in `NotebookExplorer` --- Flow.Launcher.Plugin.OneNote/Main.cs | 2 +- .../Search/Rework/SearchManager.cs | 30 ++++++++++++------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/Flow.Launcher.Plugin.OneNote/Main.cs b/Flow.Launcher.Plugin.OneNote/Main.cs index fd13031..52daf01 100644 --- a/Flow.Launcher.Plugin.OneNote/Main.cs +++ b/Flow.Launcher.Plugin.OneNote/Main.cs @@ -4,9 +4,9 @@ using System.Threading.Tasks; using System.Windows.Controls; using Flow.Launcher.Plugin.OneNote.Icons; -using Flow.Launcher.Plugin.OneNote.Search; using Flow.Launcher.Plugin.OneNote.UI.Views; using OneNoteApp = LinqToOneNote.OneNote; +using SearchManager = Flow.Launcher.Plugin.OneNote.Search.Rework.SearchManager; namespace Flow.Launcher.Plugin.OneNote { diff --git a/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs b/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs index f2010e2..c51e7f7 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs @@ -13,13 +13,16 @@ public class SearchManager { private readonly OrderedDictionary searchFunctions; //TODO: mutable keys beware/ need to fix private readonly Keywords keywords; - public RootCache RootCache { get; } = new RootCache(); + public RootCache RootCache { get; } public SearchManager(PluginInitContext context, Settings settings, ResultCreator resultCreator) { - keywords = settings.Keywords; var standardSearch = new StandardSearch(context, settings, resultCreator); var titleSearch = new TitleSearch(context, settings, resultCreator); + + RootCache = new RootCache(); + keywords = settings.Keywords; + searchFunctions = new OrderedDictionary() { { keywords.NotebookExplorer, new NotebookExplorer(context, settings, resultCreator, titleSearch, standardSearch) }, @@ -163,8 +166,6 @@ public class NotebookExplorer(PluginInitContext context, Settings settings, Resu public override List GetResults(ref SearchData searchData) { - Debug.Assert(!string.IsNullOrWhiteSpace(searchData.Search), "Uh oh spaghetti oh not supposed to have a value yoooou. \"string.IsNullOrWhiteSpace(searchData.Search)\""); - //TODO: check if searchData.search is empty because it should never be, it would got to EmptySearch if (!ValidateSearch(ref searchData)) return resultCreator.InvalidQuery(false); @@ -172,13 +173,17 @@ public override List GetResults(ref SearchData searchData) return ResultCreator.SingleResult("Failed at parsing keywords", string.Empty, string.Empty); IOneNoteItem? parent = searchData.Parent; - var results = (keyword, parent) switch + SearchFunction searchFunction = (keyword, parent) switch { - (not null, not Page) when keyword == keywords.TitleSearch => titleSearch.GetResults(ref searchData), - (not null, INotebookOrSectionGroup) when keyword == keywords.ScopedSearch => standardSearch.GetResults(ref searchData), - _ => explorer.GetResults(ref searchData), + (not null, not Page) when keyword == keywords.TitleSearch => titleSearch, + (not null, INotebookOrSectionGroup) when keyword == keywords.ScopedSearch => standardSearch, + _ => explorer, }; + var results = string.IsNullOrWhiteSpace(searchData.Search) + ? searchFunction.EmptySearch(ref searchData) + : searchFunction.GetResults(ref searchData); + if (parent == null) return results; @@ -192,7 +197,8 @@ public override List GetResults(ref SearchData searchData) private bool ValidateSearch(ref SearchData searchData) { (var search, IOneNoteItem? parent, IEnumerable? collection, _) = searchData; - Debug.Assert(collection != null, nameof(collection) + " != null"); + + //From SearchManager.Query SearchData //parent is null here //collection is rootCache.Root.Notebooks as this functions validates the search with the whole query; @@ -203,7 +209,7 @@ private bool ValidateSearch(ref SearchData searchData) while (currIndex != -1) { var itemName = search[prevIndex..currIndex]; - parent = collection.FirstOrDefault(item => item.Name == itemName); + parent = collection?.FirstOrDefault(item => item.Name == itemName); if (parent == null) return false; @@ -229,6 +235,7 @@ public override List GetResults(ref SearchData searchData) var (search, parent, collection, sortOrder) = searchData; Debug.Assert(collection != null, nameof(collection) + " != null"); + var searchResults = collection.FilterBySettings(settings) .FuzzySearch(search, context); var results = sortOrder switch @@ -279,7 +286,8 @@ public override List EmptySearch(ref SearchData searchData) } } } - + //Parent => parent of the current item/search area + //Collection => children of the parent, if parent == null; Collection = rootCache.Root.Notebooks public record struct SearchData(string Search, IOneNoteItem? Parent, IEnumerable? Collection, SortOrder SortOrder); public enum SortOrder From 75390290b524b257c4ffffc81b29899ecb08dfb2 Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Sun, 12 Apr 2026 01:03:26 +0100 Subject: [PATCH 14/24] Fix notebooks not sorting --- Flow.Launcher.Plugin.OneNote/ResultCreator.cs | 10 +++-- .../Search/Rework/SearchManager.cs | 37 ++++++++++++------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/Flow.Launcher.Plugin.OneNote/ResultCreator.cs b/Flow.Launcher.Plugin.OneNote/ResultCreator.cs index d054228..577f317 100644 --- a/Flow.Launcher.Plugin.OneNote/ResultCreator.cs +++ b/Flow.Launcher.Plugin.OneNote/ResultCreator.cs @@ -224,7 +224,7 @@ await Task.Run(() => public Result CreatePageResult(Page page, string query) => CreateOneNoteItemResult(page, false, string.IsNullOrWhiteSpace(query) ? null : context.API.FuzzySearch(query, page.Name).MatchData); - public Result CreateRecentPageResult(Page page) //TODO: make it accept any IOneNoteItem type + public Result CreateRecentPageResult(Page page) //TODO: make it accept any IOneNoteItem type and Remove { var result = CreateOneNoteItemResult(page, false); result.SubTitle = $"{page.LastModified.Humanize()} | {result.SubTitle}"; @@ -233,10 +233,12 @@ public Result CreateRecentPageResult(Page page) //TODO: make it accept any IOneN return result; } - public Result CreateRecentItemResult(IOneNoteItem item, List? highlightData = null) + public Result CreateRecentItemResult(IOneNoteItem item, bool actionIsAutoComplete, List? highlightData = null) { - var result = CreateOneNoteItemResult(item, false, highlightData); - result.SubTitle = $"{item.LastModified.Humanize()} | {result.SubTitle}"; + var result = CreateOneNoteItemResult(item, actionIsAutoComplete, highlightData); + result.SubTitle = string.IsNullOrWhiteSpace(result.SubTitle) + ? $"{item.LastModified.Humanize()}" + : $"{item.LastModified.Humanize()} | {result.SubTitle}"; if(item is Page) result.IcoPath = iconProvider.Recent; result.AddSelectedCount = false; diff --git a/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs b/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs index c51e7f7..b801929 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs @@ -13,6 +13,7 @@ public class SearchManager { private readonly OrderedDictionary searchFunctions; //TODO: mutable keys beware/ need to fix private readonly Keywords keywords; + private readonly PluginInitContext context; public RootCache RootCache { get; } public SearchManager(PluginInitContext context, Settings settings, ResultCreator resultCreator) @@ -42,7 +43,7 @@ public static bool TryParseSearch(ref SearchData searchData, [NotNullWhen(true)] continue; keyword = key; - if ( /*keyword != keywords.NotebookExplorer &&*/ search[key.Length..].StartsWith(keywords.RecentPages)) //TODO maybe allow NotebookExplorer+ RecentPAges? + if (keyword != keywords.NotebookExplorer && search[key.Length..].StartsWith(keywords.RecentPages)) //TODO maybe allow NotebookExplorer+ RecentPAges? { searchData.Search = search[(key.Length + keywords.RecentPages.Length)..]; searchData.SortOrder = SortOrder.RecentlyModified; @@ -106,7 +107,7 @@ public override List GetResults(ref SearchData searchData) return sortOrder switch { SortOrder.RecentlyModified => pages.OrderByDescending(pg => pg.LastModified) - .Select(pg => resultCreator.CreateRecentItemResult(pg)) + .Select(pg => resultCreator.CreateRecentItemResult(pg, false)) .ToList(), _ => pages.Select(pg => resultCreator.CreatePageResult(pg, search)).ToList() }; @@ -120,7 +121,7 @@ public override List EmptySearch(ref SearchData searchData) .GetAllPages() .FilterBySettings(settings) .OrderByDescending(pg => pg.LastModified) - .Select(pg => resultCreator.CreateRecentItemResult(pg)) + .Select(pg => resultCreator.CreateRecentItemResult(pg, false)) .ToList(), _ => resultCreator.SearchType("Now searching all pages", searchData.Parent?.Name), }; @@ -139,7 +140,7 @@ public override List GetResults(ref SearchData searchData) return sortOrder switch { SortOrder.RecentlyModified => results.OrderByDescending(sr => sr.Item.LastModified) - .Select(sr => resultCreator.CreateRecentItemResult(sr.Item, sr.HighlightData)) + .Select(sr => resultCreator.CreateRecentItemResult(sr.Item, false, sr.HighlightData)) .ToList(), _ => results.Select(sr => resultCreator.CreateOneNoteItemResult(sr.Item, false, sr.HighlightData, sr.Score)) .ToList(), @@ -194,7 +195,7 @@ public override List GetResults(ref SearchData searchData) return results; } - private bool ValidateSearch(ref SearchData searchData) + private static bool ValidateSearch(ref SearchData searchData) { (var search, IOneNoteItem? parent, IEnumerable? collection, _) = searchData; @@ -236,14 +237,13 @@ public override List GetResults(ref SearchData searchData) Debug.Assert(collection != null, nameof(collection) + " != null"); - var searchResults = collection.FilterBySettings(settings) - .FuzzySearch(search, context); + var searchResults = collection.FilterBySettings(settings).FuzzySearch(search, context); var results = sortOrder switch { SortOrder.RecentlyModified => searchResults.OrderByDescending(sr => sr.Item.LastModified) - .Select(sr => resultCreator.CreateRecentItemResult(sr.Item, sr.HighlightData)) + .Select(sr => resultCreator.CreateRecentItemResult(sr.Item, true, sr.HighlightData)) .ToList(), - _ => searchResults.Select(r => resultCreator.CreateOneNoteItemResult(r.Item, true, r.HighlightData, r.Score)) + _ => searchResults.Select(sr => resultCreator.CreateOneNoteItemResult(sr.Item, true, sr.HighlightData, sr.Score)) .ToList(), }; @@ -277,12 +277,21 @@ public override List GetResults(ref SearchData searchData) public override List EmptySearch(ref SearchData searchData) { - Debug.Assert(searchData.Collection != null); - var results = searchData.Collection.FilterBySettings(settings) - .Select(item => resultCreator.CreateOneNoteItemResult(item, true)) - .ToList(); + var (_, parent, collection, sortOrder) = searchData; + + Debug.Assert(collection != null); + var results = sortOrder switch + { + SortOrder.RecentlyModified => collection.FilterBySettings(settings) + .OrderByDescending(item => item.LastModified) + .Select(item => resultCreator.CreateRecentItemResult(item, true)) + .ToList(), + _ => collection.FilterBySettings(settings) + .Select(item => resultCreator.CreateOneNoteItemResult(item, true)) + .ToList(), + }; - return results.Count != 0 ? results : resultCreator.EmptyCollection(results, searchData.Parent); + return results.Count != 0 ? results : resultCreator.EmptyCollection(results, parent); } } } From 7ee9bd05d1588074b0ae950722d1f1e2bf653d7c Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Mon, 13 Apr 2026 02:10:18 +0100 Subject: [PATCH 15/24] Refactor again --- Flow.Launcher.Plugin.OneNote/Keywords.cs | 10 ++ .../Search/Rework/SearchManager.cs | 117 ++++++++++-------- 2 files changed, 72 insertions(+), 55 deletions(-) diff --git a/Flow.Launcher.Plugin.OneNote/Keywords.cs b/Flow.Launcher.Plugin.OneNote/Keywords.cs index c8988dd..908a9c0 100644 --- a/Flow.Launcher.Plugin.OneNote/Keywords.cs +++ b/Flow.Launcher.Plugin.OneNote/Keywords.cs @@ -1,4 +1,6 @@ using System; +using System.Collections; +using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; @@ -12,6 +14,14 @@ public class Keywords public Keyword RecentPages { get; set; } = new ("rp:"); public Keyword TitleSearch { get; set; } = new ("*"); public Keyword ScopedSearch { get; set; } = new (">"); + + private Keyword[]? actionKeywords; + // /// + // /// Action Keywords i.e. keywords that aren't a modifier, i.e. everything but (as of right now) + // /// + //Replace with fast reflection. + //TODO: make class IEnumerable, Dictionary or IList if recentpages is used, else use fast reflectiono + public Keyword[] ActionKeywords => actionKeywords ??= [NotebookExplorer, TitleSearch, ScopedSearch, RecentPages]; } [JsonConverter(typeof(KeywordJsonConverter))] diff --git a/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs b/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs index b801929..aa0539c 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Linq; using LinqToOneNote; using LinqToOneNote.Abstractions; @@ -11,54 +10,58 @@ namespace Flow.Launcher.Plugin.OneNote.Search.Rework { public class SearchManager { - private readonly OrderedDictionary searchFunctions; //TODO: mutable keys beware/ need to fix private readonly Keywords keywords; - private readonly PluginInitContext context; + private readonly NotebookExplorer notebookExplorer; + private readonly StandardSearch standardSearch; + private readonly TitleSearch titleSearch; public RootCache RootCache { get; } public SearchManager(PluginInitContext context, Settings settings, ResultCreator resultCreator) { - var standardSearch = new StandardSearch(context, settings, resultCreator); - var titleSearch = new TitleSearch(context, settings, resultCreator); + standardSearch = new StandardSearch(context, settings, resultCreator); + titleSearch = new TitleSearch(context, settings, resultCreator); + notebookExplorer = new NotebookExplorer(context, settings, resultCreator, titleSearch, standardSearch); RootCache = new RootCache(); keywords = settings.Keywords; - - searchFunctions = new OrderedDictionary() - { - { keywords.NotebookExplorer, new NotebookExplorer(context, settings, resultCreator, titleSearch, standardSearch) }, - { keywords.TitleSearch, titleSearch }, - { Keyword.Empty, standardSearch }, - }; } - - //Get the keyword - public static bool TryParseSearch(ref SearchData searchData, [NotNullWhen(true)] out Keyword? keyword, IList keywordsToMatch, Keywords keywords) + /// + /// Gets the keyword from the search, and removes it from the search string. Also gets the sort order + /// + /// + /// + /// + public static Keyword? GetKeyword(ref SearchData searchData, Keywords keywords) { - var search = searchData.Search; - for (var i = 0; i < keywordsToMatch.Count; i++) + Keyword? keyword = null; + var searchSpan = searchData.Search.AsSpan(); + + for (var i = 0; i < keywords.ActionKeywords.Length; i++) { - Keyword key = keywordsToMatch[i]; - if (!search.StartsWithOrd(key)) + Keyword key = keywords.ActionKeywords[i]; + if (!StartsWithKeyword(ref searchSpan, key)) continue; - + keyword = key; - if (keyword != keywords.NotebookExplorer && search[key.Length..].StartsWith(keywords.RecentPages)) //TODO maybe allow NotebookExplorer+ RecentPAges? + break; + } + + searchData.SortOrder = keyword == keywords.RecentPages || StartsWithKeyword(ref searchSpan, keywords.RecentPages) + ? SortOrder.RecentlyModified + : SortOrder.Default; + + searchData.Search = searchSpan.ToString(); + return keyword; + + static bool StartsWithKeyword(ref ReadOnlySpan searchSpan, Keyword keyword) //TODO: make extension method + { + if (searchSpan.StartsWith(keyword.Value.AsSpan(), StringComparison.Ordinal)) { - searchData.Search = search[(key.Length + keywords.RecentPages.Length)..]; - searchData.SortOrder = SortOrder.RecentlyModified; + searchSpan = searchSpan[keyword.Length..]; return true; } - - searchData.Search = search[key.Length..]; - searchData.SortOrder = SortOrder.Default; - return true; + return false; } - - //Should never fail; - //context.API.LogWarn(nameof(SearchManager), $"Could not parse keywords from search: {search}. This should never happen..."); - keyword = null; - return false; } public List Query(Query query) @@ -69,14 +72,17 @@ public List Query(Query query) } var searchData = new SearchData(query.Search, null, RootCache.Root.Notebooks, SortOrder.Default); - if (!TryParseSearch(ref searchData, out var keyword, searchFunctions.Keys, keywords)) - return ResultCreator.SingleResult("Failed at parsing keywords", string.Empty, string.Empty); - - var searchFunction = searchFunctions[keyword]; + + Keyword? keyword = GetKeyword(ref searchData, keywords); + + SearchFunction searchFunction = keyword switch + { + not null when keyword == keywords.NotebookExplorer => notebookExplorer, + not null when keyword == keywords.TitleSearch => titleSearch, + _ => standardSearch, + }; - var results = string.IsNullOrWhiteSpace(searchData.Search) - ? searchFunction.EmptySearch(ref searchData) - : searchFunction.GetResults(ref searchData); + var results = searchFunction.GetResults(ref searchData); return results.Count != 0 ? results : ResultCreator.NoMatchesFound(); } @@ -87,13 +93,18 @@ public abstract class SearchFunction(PluginInitContext context, Settings setting protected readonly ResultCreator resultCreator = resultCreator; protected readonly Settings settings = settings; protected readonly PluginInitContext context = context; - public abstract List GetResults(ref SearchData searchData); - public abstract List EmptySearch(ref SearchData searchData); + protected abstract List GetResultsInternal(ref SearchData searchData); + protected abstract List EmptySearch(ref SearchData searchData); + + public List GetResults(ref SearchData searchData) => + string.IsNullOrWhiteSpace(searchData.Search) + ? EmptySearch(ref searchData) + : GetResultsInternal(ref searchData); } public class StandardSearch(PluginInitContext context, Settings settings, ResultCreator resultCreator) : SearchFunction(context, settings, resultCreator) { - public override List GetResults(ref SearchData searchData) + protected override List GetResultsInternal(ref SearchData searchData) { var (search, parent, _, sortOrder) = searchData; @@ -113,7 +124,7 @@ public override List GetResults(ref SearchData searchData) }; } - public override List EmptySearch(ref SearchData searchData) + protected override List EmptySearch(ref SearchData searchData) { return searchData.SortOrder switch { @@ -130,7 +141,7 @@ public override List EmptySearch(ref SearchData searchData) public class TitleSearch(PluginInitContext context, Settings settings, ResultCreator resultCreator) : SearchFunction(context, settings, resultCreator) { - public override List GetResults(ref SearchData searchData) + protected override List GetResultsInternal(ref SearchData searchData) { var (search, _, collection, sortOrder) = searchData; var results = collection.Descendants() @@ -147,7 +158,7 @@ public override List GetResults(ref SearchData searchData) }; } - public override List EmptySearch(ref SearchData searchData) + protected override List EmptySearch(ref SearchData searchData) { const string emptySearchPrefix = "Now searching by title"; return searchData.SortOrder switch @@ -163,15 +174,13 @@ public class NotebookExplorer(PluginInitContext context, Settings settings, Resu { private readonly Explorer explorer = new(context, settings, resultCreator); private readonly Keywords keywords = settings.Keywords; - private readonly Keyword[] keywordsToMatch = [settings.Keywords.TitleSearch, settings.Keywords.ScopedSearch, Keyword.Empty]; - public override List GetResults(ref SearchData searchData) + protected override List GetResultsInternal(ref SearchData searchData) { if (!ValidateSearch(ref searchData)) return resultCreator.InvalidQuery(false); - if(!SearchManager.TryParseSearch(ref searchData, out Keyword? keyword, keywordsToMatch, keywords)) - return ResultCreator.SingleResult("Failed at parsing keywords", string.Empty, string.Empty); + Keyword? keyword = SearchManager.GetKeyword(ref searchData, keywords); IOneNoteItem? parent = searchData.Parent; SearchFunction searchFunction = (keyword, parent) switch @@ -181,9 +190,7 @@ public override List GetResults(ref SearchData searchData) _ => explorer, }; - var results = string.IsNullOrWhiteSpace(searchData.Search) - ? searchFunction.EmptySearch(ref searchData) - : searchFunction.GetResults(ref searchData); + var results = searchFunction.GetResults(ref searchData); if (parent == null) return results; @@ -226,12 +233,12 @@ private static bool ValidateSearch(ref SearchData searchData) return true; } - public override List EmptySearch(ref SearchData searchData) => explorer.EmptySearch(ref searchData); + protected override List EmptySearch(ref SearchData searchData) => explorer.GetResults(ref searchData); private class Explorer(PluginInitContext context, Settings settings, ResultCreator resultCreator) : SearchFunction(context, settings, resultCreator) { - public override List GetResults(ref SearchData searchData) + protected override List GetResultsInternal(ref SearchData searchData) { var (search, parent, collection, sortOrder) = searchData; @@ -275,7 +282,7 @@ public override List GetResults(ref SearchData searchData) return results; } - public override List EmptySearch(ref SearchData searchData) + protected override List EmptySearch(ref SearchData searchData) { var (_, parent, collection, sortOrder) = searchData; From 66ac6a29e55c969d75007d046cb644a3ce0ce0de Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Mon, 13 Apr 2026 19:56:50 +0100 Subject: [PATCH 16/24] Implement the search rework --- Flow.Launcher.Plugin.OneNote/Main.cs | 2 +- Flow.Launcher.Plugin.OneNote/ResultCreator.cs | 14 +- .../Search/NotebookExplorer.cs | 145 ++++---- .../Search/RecentPages.cs | 38 --- .../Search/Rework/SearchManager.cs | 314 ------------------ .../Search/SearchBase.cs | 30 +- .../Search/SearchExtensions.cs | 78 +++-- .../Search/SearchManager.cs | 31 +- .../Search/StandardSearch.cs | 51 +-- .../Search/TitleSearch.cs | 44 +-- 10 files changed, 223 insertions(+), 524 deletions(-) delete mode 100644 Flow.Launcher.Plugin.OneNote/Search/RecentPages.cs delete mode 100644 Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs diff --git a/Flow.Launcher.Plugin.OneNote/Main.cs b/Flow.Launcher.Plugin.OneNote/Main.cs index 52daf01..fd13031 100644 --- a/Flow.Launcher.Plugin.OneNote/Main.cs +++ b/Flow.Launcher.Plugin.OneNote/Main.cs @@ -4,9 +4,9 @@ using System.Threading.Tasks; using System.Windows.Controls; using Flow.Launcher.Plugin.OneNote.Icons; +using Flow.Launcher.Plugin.OneNote.Search; using Flow.Launcher.Plugin.OneNote.UI.Views; using OneNoteApp = LinqToOneNote.OneNote; -using SearchManager = Flow.Launcher.Plugin.OneNote.Search.Rework.SearchManager; namespace Flow.Launcher.Plugin.OneNote { diff --git a/Flow.Launcher.Plugin.OneNote/ResultCreator.cs b/Flow.Launcher.Plugin.OneNote/ResultCreator.cs index 577f317..3190d31 100644 --- a/Flow.Launcher.Plugin.OneNote/ResultCreator.cs +++ b/Flow.Launcher.Plugin.OneNote/ResultCreator.cs @@ -220,19 +220,7 @@ await Task.Run(() => }, }; } - - public Result CreatePageResult(Page page, string query) - => CreateOneNoteItemResult(page, false, string.IsNullOrWhiteSpace(query) ? null : context.API.FuzzySearch(query, page.Name).MatchData); - public Result CreateRecentPageResult(Page page) //TODO: make it accept any IOneNoteItem type and Remove - { - var result = CreateOneNoteItemResult(page, false); - result.SubTitle = $"{page.LastModified.Humanize()} | {result.SubTitle}"; - result.IcoPath = iconProvider.Recent; - result.AddSelectedCount = false; - return result; - } - public Result CreateRecentItemResult(IOneNoteItem item, bool actionIsAutoComplete, List? highlightData = null) { var result = CreateOneNoteItemResult(item, actionIsAutoComplete, highlightData); @@ -418,7 +406,7 @@ public List SearchType(string title, string? parentName, string subTitle return SingleResult(title, subTitle, iconProvider.Search); } - public static List SingleResult(string title, string subTitle, string iconPath) + private static List SingleResult(string title, string subTitle, string iconPath) { return new List { diff --git a/Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs b/Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs index 5b6971e..880c3e1 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs @@ -1,48 +1,53 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using LinqToOneNote; using LinqToOneNote.Abstractions; -using OneNoteApp = LinqToOneNote.OneNote; namespace Flow.Launcher.Plugin.OneNote.Search { - public class NotebookExplorer(PluginInitContext context, Settings settings, ResultCreator resultCreator, RootCache rootCache) - : SearchBase(context, settings, resultCreator, settings.Keywords.NotebookExplorer) + public class NotebookExplorer(PluginInitContext context, Settings settings, ResultCreator resultCreator, TitleSearch titleSearch, StandardSearch standardSearch) + : SearchBase(context, settings, resultCreator) { - private readonly TitleSearch titleSearch = new (context, settings, resultCreator, rootCache); - private readonly StandardSearch standardSearch = new(context, settings, resultCreator); - public override List GetResults(Query query) + private readonly Explorer explorer = new(context, settings, resultCreator); + private readonly Keywords keywords = settings.Keywords; + + protected override List GetResultsInternal(ref SearchData searchData) { - if (!ValidateSearch(query, out string? search, out IOneNoteItem? parent, out IEnumerable collection)) + if (!ValidateSearch(ref searchData)) return resultCreator.InvalidQuery(false); - List results = search switch + Keyword? keyword = SearchExtensions.GetKeyword(ref searchData, keywords); + + IOneNoteItem? parent = searchData.Parent; + SearchBase searchBase = (keyword, parent) switch { - { } when search.StartsWithOrd(Keywords.TitleSearch) && parent is not Page => titleSearch.GetResults(search, parent, collection), - { } when search.StartsWithOrd(Keywords.ScopedSearch) && parent is INotebookOrSectionGroup => standardSearch.GetResults(query, parent), - { } when !string.IsNullOrWhiteSpace(search) => Explorer(search, parent, collection), - _ => ShowAll(parent, collection), + (not null, not Page) when keyword == keywords.TitleSearch => titleSearch, + (not null, INotebookOrSectionGroup) when keyword == keywords.ScopedSearch => standardSearch, + _ => explorer, }; + var results = searchBase.GetResults(ref searchData); + if (parent == null) return results; Result result = resultCreator.CreateOneNoteItemResult(parent, false, score: Result.MaxScore); result.Title = $"Open \"{parent.Name}\" in OneNote"; - result.SubTitle = $"Use \'{Keywords.ScopedSearch}\' to search this item. Use \'{Keywords.TitleSearch}\' to search by title in this item"; + result.SubTitle = $"Use \'{keywords.ScopedSearch}\' to search this item. Use \'{keywords.TitleSearch}\' to search by title in this item. Use \'{keywords.RecentPages}\' to sort by recently modified"; results.Add(result); return results; } - - private bool ValidateSearch(Query query, out string? lastSearch, out IOneNoteItem? parent, out IEnumerable collection) + + private static bool ValidateSearch(ref SearchData searchData) { - lastSearch = null; - parent = null; - - collection = rootCache.Root.Notebooks; + (var search, IOneNoteItem? parent, IEnumerable? collection, _) = searchData; + + //From SearchManager.Query SearchData + //parent is null here + //collection is rootCache.Root.Notebooks as this functions validates the search with the whole query; - string search = query.Search[(query.Search.IndexOf(Keywords.NotebookExplorer, StringComparison.Ordinal) + Keywords.NotebookExplorer.Length)..]; const string separator = Keywords.NotebookExplorerSeparator; var currIndex = search.IndexOf(separator, StringComparison.Ordinal); var prevIndex = 0; @@ -50,7 +55,7 @@ private bool ValidateSearch(Query query, out string? lastSearch, out IOneNoteIte while (currIndex != -1) { var itemName = search[prevIndex..currIndex]; - parent = collection.FirstOrDefault(item => item.Name == itemName); + parent = collection?.FirstOrDefault(item => item.Name == itemName); if (parent == null) return false; @@ -60,52 +65,78 @@ private bool ValidateSearch(Query query, out string? lastSearch, out IOneNoteIte currIndex = search.IndexOf(separator, currIndex + separator.Length, StringComparison.Ordinal); } - lastSearch = search[prevIndex..]; + searchData.Search = search[prevIndex..]; + searchData.Parent = parent; + searchData.Collection = collection; return true; } + + protected override List EmptySearch(ref SearchData searchData) => explorer.GetResults(ref searchData); - private List ShowAll(IOneNoteItem? parent, IEnumerable collection) - { - var results = collection.FilterBySettings(settings) - .Select(item => resultCreator.CreateOneNoteItemResult(item, true)) - .ToList(); - - return results.Count != 0 ? results : resultCreator.EmptyCollection(results, parent); - } - - private List Explorer(string search, IOneNoteItem? parent, IEnumerable collection) + private class Explorer(PluginInitContext context, Settings settings, ResultCreator resultCreator) : SearchBase(context, settings, resultCreator) { - var results = collection.FilterBySettings(settings) - .FuzzySearch(search, context) - .Select(r => resultCreator.CreateOneNoteItemResult(r.Item, true, r.HighlightData, r.Score)) - .ToList(); + protected override List GetResultsInternal(ref SearchData searchData) + { + var (search, parent, collection, sortOrder) = searchData; - // If parent is a section, pages inside can have the same name - if (parent is not Section && results.Any(result => string.Equals(search.Trim(), result.Title, StringComparison.OrdinalIgnoreCase))) - return results; + Debug.Assert(collection != null, nameof(collection) + " != null"); + + var searchResults = collection.FilterBySettings(settings).FuzzySearch(search, context); + var results = sortOrder switch + { + SortOrder.RecentlyModified => searchResults.OrderByDescending(sr => sr.Item.LastModified) + .Select(sr => resultCreator.CreateRecentItemResult(sr.Item, true, sr.HighlightData)) + .ToList(), + _ => searchResults.Select(sr => resultCreator.CreateOneNoteItemResult(sr.Item, true, sr.HighlightData, sr.Score)) + .ToList(), + }; + + // If parent is a section, pages inside can have the same name + if (parent is not Section && results.Any(result => string.Equals(search.Trim(), result.Title, StringComparison.OrdinalIgnoreCase))) + return results; + + if (parent?.IsInRecycleBin() == true) + return results; + + //Add option to create new items + switch (parent) + { + case null: + results.Add(resultCreator.CreateNewNotebookResult(search)); + break; + case INotebookOrSectionGroup x: + results.Add(resultCreator.CreateNewSectionResult(search, x)); + results.Add(resultCreator.CreateNewSectionGroupResult(search, x)); + break; + case Section section: + if (!section.Locked) + { + results.Add(resultCreator.CreateNewPageResult(search, section)); + } + break; + } - if (parent?.IsInRecycleBin() == true) return results; + } - //Add option to create new items - switch (parent) + protected override List EmptySearch(ref SearchData searchData) { - case null: - results.Add(resultCreator.CreateNewNotebookResult(search)); - break; - case INotebookOrSectionGroup x: - results.Add(resultCreator.CreateNewSectionResult(search, x)); - results.Add(resultCreator.CreateNewSectionGroupResult(search, x)); - break; - case Section section: - if (!section.Locked) - { - results.Add(resultCreator.CreateNewPageResult(search, section)); - } - break; + var (_, parent, collection, sortOrder) = searchData; + + Debug.Assert(collection != null); + var results = sortOrder switch + { + SortOrder.RecentlyModified => collection.FilterBySettings(settings) + .OrderByDescending(item => item.LastModified) + .Select(item => resultCreator.CreateRecentItemResult(item, true)) + .ToList(), + _ => collection.FilterBySettings(settings) + .Select(item => resultCreator.CreateOneNoteItemResult(item, true)) + .ToList(), + }; + + return results.Count != 0 ? results : resultCreator.EmptyCollection(results, parent); } - - return results; } } } \ No newline at end of file diff --git a/Flow.Launcher.Plugin.OneNote/Search/RecentPages.cs b/Flow.Launcher.Plugin.OneNote/Search/RecentPages.cs deleted file mode 100644 index 7b1131e..0000000 --- a/Flow.Launcher.Plugin.OneNote/Search/RecentPages.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using LinqToOneNote; -using OneNoteApp = LinqToOneNote.OneNote; - -namespace Flow.Launcher.Plugin.OneNote.Search -{ - public class RecentPages(PluginInitContext context, Settings settings, ResultCreator resultCreator, RootCache rootCache) - : SearchBase(context, settings, resultCreator, settings.Keywords.RecentPages) - { - public override List GetResults(Query query) - { - int count = settings.DefaultRecentsCount; - string search = query.Search; - if (search.Length > keyword.Length && int.TryParse(search[keyword.Length..], out int userChosenCount)) - count = userChosenCount; - - string currentSearch = query.Search[keyword.Length..]; - if (!string.IsNullOrWhiteSpace(currentSearch)) - { - return OneNoteApp.FindPages(currentSearch) - .FilterBySettings(settings) - .OrderByDescending(pg => pg.LastModified) - .Select(resultCreator.CreateRecentPageResult) - .ToList(); - } - - return rootCache.Root - .Notebooks - .GetAllPages() - .FilterBySettings(settings) - .OrderByDescending(pg => pg.LastModified) - .Take(count) - .Select(resultCreator.CreateRecentPageResult) - .ToList(); - } - } -} \ No newline at end of file diff --git a/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs b/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs deleted file mode 100644 index aa0539c..0000000 --- a/Flow.Launcher.Plugin.OneNote/Search/Rework/SearchManager.cs +++ /dev/null @@ -1,314 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using LinqToOneNote; -using LinqToOneNote.Abstractions; -using OneNoteApp = LinqToOneNote.OneNote; - -namespace Flow.Launcher.Plugin.OneNote.Search.Rework -{ - public class SearchManager - { - private readonly Keywords keywords; - private readonly NotebookExplorer notebookExplorer; - private readonly StandardSearch standardSearch; - private readonly TitleSearch titleSearch; - public RootCache RootCache { get; } - - public SearchManager(PluginInitContext context, Settings settings, ResultCreator resultCreator) - { - standardSearch = new StandardSearch(context, settings, resultCreator); - titleSearch = new TitleSearch(context, settings, resultCreator); - notebookExplorer = new NotebookExplorer(context, settings, resultCreator, titleSearch, standardSearch); - - RootCache = new RootCache(); - keywords = settings.Keywords; - } - /// - /// Gets the keyword from the search, and removes it from the search string. Also gets the sort order - /// - /// - /// - /// - public static Keyword? GetKeyword(ref SearchData searchData, Keywords keywords) - { - Keyword? keyword = null; - var searchSpan = searchData.Search.AsSpan(); - - for (var i = 0; i < keywords.ActionKeywords.Length; i++) - { - Keyword key = keywords.ActionKeywords[i]; - if (!StartsWithKeyword(ref searchSpan, key)) - continue; - - keyword = key; - break; - } - - searchData.SortOrder = keyword == keywords.RecentPages || StartsWithKeyword(ref searchSpan, keywords.RecentPages) - ? SortOrder.RecentlyModified - : SortOrder.Default; - - searchData.Search = searchSpan.ToString(); - return keyword; - - static bool StartsWithKeyword(ref ReadOnlySpan searchSpan, Keyword keyword) //TODO: make extension method - { - if (searchSpan.StartsWith(keyword.Value.AsSpan(), StringComparison.Ordinal)) - { - searchSpan = searchSpan[keyword.Length..]; - return true; - } - return false; - } - } - - public List Query(Query query) - { - if (query.IsReQuery) - { - RootCache.SetDirty(); - } - - var searchData = new SearchData(query.Search, null, RootCache.Root.Notebooks, SortOrder.Default); - - Keyword? keyword = GetKeyword(ref searchData, keywords); - - SearchFunction searchFunction = keyword switch - { - not null when keyword == keywords.NotebookExplorer => notebookExplorer, - not null when keyword == keywords.TitleSearch => titleSearch, - _ => standardSearch, - }; - - var results = searchFunction.GetResults(ref searchData); - - return results.Count != 0 ? results : ResultCreator.NoMatchesFound(); - } - } - - public abstract class SearchFunction(PluginInitContext context, Settings settings, ResultCreator resultCreator) - { - protected readonly ResultCreator resultCreator = resultCreator; - protected readonly Settings settings = settings; - protected readonly PluginInitContext context = context; - protected abstract List GetResultsInternal(ref SearchData searchData); - protected abstract List EmptySearch(ref SearchData searchData); - - public List GetResults(ref SearchData searchData) => - string.IsNullOrWhiteSpace(searchData.Search) - ? EmptySearch(ref searchData) - : GetResultsInternal(ref searchData); - } - - public class StandardSearch(PluginInitContext context, Settings settings, ResultCreator resultCreator) : SearchFunction(context, settings, resultCreator) - { - protected override List GetResultsInternal(ref SearchData searchData) - { - var (search, parent, _, sortOrder) = searchData; - - if (!char.IsLetterOrDigit(search[0])) - return resultCreator.InvalidQuery(); - - IEnumerable pages = parent is null - ? OneNoteApp.FindPages(search) - : OneNoteApp.FindPages(search, parent); - - return sortOrder switch - { - SortOrder.RecentlyModified => pages.OrderByDescending(pg => pg.LastModified) - .Select(pg => resultCreator.CreateRecentItemResult(pg, false)) - .ToList(), - _ => pages.Select(pg => resultCreator.CreatePageResult(pg, search)).ToList() - }; - } - - protected override List EmptySearch(ref SearchData searchData) - { - return searchData.SortOrder switch - { - SortOrder.RecentlyModified => searchData.Collection - .GetAllPages() - .FilterBySettings(settings) - .OrderByDescending(pg => pg.LastModified) - .Select(pg => resultCreator.CreateRecentItemResult(pg, false)) - .ToList(), - _ => resultCreator.SearchType("Now searching all pages", searchData.Parent?.Name), - }; - } - } - - public class TitleSearch(PluginInitContext context, Settings settings, ResultCreator resultCreator) : SearchFunction(context, settings, resultCreator) - { - protected override List GetResultsInternal(ref SearchData searchData) - { - var (search, _, collection, sortOrder) = searchData; - var results = collection.Descendants() - .FilterBySettings(settings) - .FuzzySearch(search, context); - - return sortOrder switch - { - SortOrder.RecentlyModified => results.OrderByDescending(sr => sr.Item.LastModified) - .Select(sr => resultCreator.CreateRecentItemResult(sr.Item, false, sr.HighlightData)) - .ToList(), - _ => results.Select(sr => resultCreator.CreateOneNoteItemResult(sr.Item, false, sr.HighlightData, sr.Score)) - .ToList(), - }; - } - - protected override List EmptySearch(ref SearchData searchData) - { - const string emptySearchPrefix = "Now searching by title"; - return searchData.SortOrder switch - { - SortOrder.RecentlyModified => resultCreator.SearchType($"{emptySearchPrefix} ordered by last modified", searchData.Parent?.Name), - _ => resultCreator.SearchType(emptySearchPrefix, searchData.Parent?.Name), - }; - } - } - - public class NotebookExplorer(PluginInitContext context, Settings settings, ResultCreator resultCreator, TitleSearch titleSearch, StandardSearch standardSearch) - : SearchFunction(context, settings, resultCreator) - { - private readonly Explorer explorer = new(context, settings, resultCreator); - private readonly Keywords keywords = settings.Keywords; - - protected override List GetResultsInternal(ref SearchData searchData) - { - if (!ValidateSearch(ref searchData)) - return resultCreator.InvalidQuery(false); - - Keyword? keyword = SearchManager.GetKeyword(ref searchData, keywords); - - IOneNoteItem? parent = searchData.Parent; - SearchFunction searchFunction = (keyword, parent) switch - { - (not null, not Page) when keyword == keywords.TitleSearch => titleSearch, - (not null, INotebookOrSectionGroup) when keyword == keywords.ScopedSearch => standardSearch, - _ => explorer, - }; - - var results = searchFunction.GetResults(ref searchData); - - if (parent == null) - return results; - - Result result = resultCreator.CreateOneNoteItemResult(parent, false, score: Result.MaxScore); - result.Title = $"Open \"{parent.Name}\" in OneNote"; - result.SubTitle = $"Use \'{keywords.ScopedSearch}\' to search this item. Use \'{keywords.TitleSearch}\' to search by title in this item. Use \'{keywords.RecentPages}\' to sort by recently modified"; - results.Add(result); - return results; - } - - private static bool ValidateSearch(ref SearchData searchData) - { - (var search, IOneNoteItem? parent, IEnumerable? collection, _) = searchData; - - //From SearchManager.Query SearchData - //parent is null here - //collection is rootCache.Root.Notebooks as this functions validates the search with the whole query; - - const string separator = Keywords.NotebookExplorerSeparator; - var currIndex = search.IndexOf(separator, StringComparison.Ordinal); - var prevIndex = 0; - - while (currIndex != -1) - { - var itemName = search[prevIndex..currIndex]; - parent = collection?.FirstOrDefault(item => item.Name == itemName); - if (parent == null) - return false; - - collection = parent.Children; - - prevIndex = currIndex + 1; - currIndex = search.IndexOf(separator, currIndex + separator.Length, StringComparison.Ordinal); - } - - searchData.Search = search[prevIndex..]; - searchData.Parent = parent; - searchData.Collection = collection; - return true; - } - - protected override List EmptySearch(ref SearchData searchData) => explorer.GetResults(ref searchData); - - - private class Explorer(PluginInitContext context, Settings settings, ResultCreator resultCreator) : SearchFunction(context, settings, resultCreator) - { - protected override List GetResultsInternal(ref SearchData searchData) - { - var (search, parent, collection, sortOrder) = searchData; - - Debug.Assert(collection != null, nameof(collection) + " != null"); - - var searchResults = collection.FilterBySettings(settings).FuzzySearch(search, context); - var results = sortOrder switch - { - SortOrder.RecentlyModified => searchResults.OrderByDescending(sr => sr.Item.LastModified) - .Select(sr => resultCreator.CreateRecentItemResult(sr.Item, true, sr.HighlightData)) - .ToList(), - _ => searchResults.Select(sr => resultCreator.CreateOneNoteItemResult(sr.Item, true, sr.HighlightData, sr.Score)) - .ToList(), - }; - - // If parent is a section, pages inside can have the same name - if (parent is not Section && results.Any(result => string.Equals(search.Trim(), result.Title, StringComparison.OrdinalIgnoreCase))) - return results; - - if (parent?.IsInRecycleBin() == true) - return results; - - //Add option to create new items - switch (parent) - { - case null: - results.Add(resultCreator.CreateNewNotebookResult(search)); - break; - case INotebookOrSectionGroup x: - results.Add(resultCreator.CreateNewSectionResult(search, x)); - results.Add(resultCreator.CreateNewSectionGroupResult(search, x)); - break; - case Section section: - if (!section.Locked) - { - results.Add(resultCreator.CreateNewPageResult(search, section)); - } - break; - } - - return results; - } - - protected override List EmptySearch(ref SearchData searchData) - { - var (_, parent, collection, sortOrder) = searchData; - - Debug.Assert(collection != null); - var results = sortOrder switch - { - SortOrder.RecentlyModified => collection.FilterBySettings(settings) - .OrderByDescending(item => item.LastModified) - .Select(item => resultCreator.CreateRecentItemResult(item, true)) - .ToList(), - _ => collection.FilterBySettings(settings) - .Select(item => resultCreator.CreateOneNoteItemResult(item, true)) - .ToList(), - }; - - return results.Count != 0 ? results : resultCreator.EmptyCollection(results, parent); - } - } - } - //Parent => parent of the current item/search area - //Collection => children of the parent, if parent == null; Collection = rootCache.Root.Notebooks - public record struct SearchData(string Search, IOneNoteItem? Parent, IEnumerable? Collection, SortOrder SortOrder); - - public enum SortOrder - { - Default = 0, - RecentlyModified = 1, - } -} \ No newline at end of file diff --git a/Flow.Launcher.Plugin.OneNote/Search/SearchBase.cs b/Flow.Launcher.Plugin.OneNote/Search/SearchBase.cs index 9662b9c..67cbc0a 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/SearchBase.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/SearchBase.cs @@ -2,29 +2,17 @@ namespace Flow.Launcher.Plugin.OneNote.Search { - public abstract class SearchBase(PluginInitContext context, Settings settings, ResultCreator resultCreator, Keyword keyword) + public abstract class SearchBase(PluginInitContext context, Settings settings, ResultCreator resultCreator) { - protected readonly PluginInitContext context = context; - protected readonly Settings settings = settings; protected readonly ResultCreator resultCreator = resultCreator; - public readonly Keyword keyword = keyword; - protected Keywords Keywords => settings.Keywords; - public abstract List GetResults(Query query); - - protected bool GetSortOrder(ref string currentSearch, out bool emptySearchOnSorted) - { - emptySearchOnSorted = false; - Keyword lastModifiedKeyword = Keywords.RecentPages; - if (!currentSearch.StartsWithOrd(lastModifiedKeyword)) - return false; - if (currentSearch.Length == lastModifiedKeyword.Length) - { - emptySearchOnSorted = true; - } - currentSearch = currentSearch[lastModifiedKeyword.Length..]; - return true; - } - + protected readonly Settings settings = settings; + protected readonly PluginInitContext context = context; + protected abstract List GetResultsInternal(ref SearchData searchData); + protected abstract List EmptySearch(ref SearchData searchData); + public List GetResults(ref SearchData searchData) => + string.IsNullOrWhiteSpace(searchData.Search) + ? EmptySearch(ref searchData) + : GetResultsInternal(ref searchData); } } \ No newline at end of file diff --git a/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs b/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs index 51bf215..9c304c4 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs @@ -6,14 +6,22 @@ namespace Flow.Launcher.Plugin.OneNote.Search { - public record SearchResult(T Item, List? HighlightData, int Score) where T : IOneNoteItem + public enum SortOrder { - public int Score { get; set; } = Score; + Default = 0, + RecentlyModified = 1, } + //Parent => parent of the current item/search area + //Collection => children of the parent, if parent == null; Collection = rootCache.Root.Notebooks + public record struct SearchData(string Search, IOneNoteItem? Parent, IEnumerable? Collection, SortOrder SortOrder); + + public record SearchResult(T Item, List? HighlightData, int Score) where T : IOneNoteItem; + public static class SearchExtensions { - public static IEnumerable> FuzzySearch(this IEnumerable source, string search, PluginInitContext context) where T: IOneNoteItem + public static IEnumerable> FuzzySearch(this IEnumerable source, string search, PluginInitContext context) + where T : IOneNoteItem { foreach (var item in source) { @@ -24,40 +32,58 @@ public static IEnumerable> FuzzySearch(this IEnumerable so } } } + public static IEnumerable FilterBySettings(this IEnumerable source, Settings settings) where T : IOneNoteItem { - foreach (var item in source) + return source.Where(item => { - var success = true; if (!settings.ShowEncrypted && item is Section section) { - success = !section.Encrypted; + return !section.Encrypted; } - if (!settings.ShowRecycleBin && item.IsInRecycleBin()) - { - success = false; - } - - if (success) - { - yield return item; - } - } + return settings.ShowRecycleBin || !item.IsInRecycleBin(); + }); } - public static IEnumerable OrderByLastModified(this IEnumerable source, bool sortByLastModified) + /// + /// Gets the keyword from the search, and removes it from the search string. Also gets the sort order + /// + /// + /// + /// + public static Keyword? GetKeyword(ref SearchData searchData, Keywords keywords) { - return sortByLastModified - ? source.OrderByDescending(result => + Keyword? keyword = null; + var searchSpan = searchData.Search.AsSpan(); + + for (var i = 0; i < keywords.ActionKeywords.Length; i++) + { + Keyword key = keywords.ActionKeywords[i]; + if (!StartsWithKeyword(ref searchSpan, key)) + continue; + + keyword = key; + break; + } + + if (keyword == keywords.RecentPages || StartsWithKeyword(ref searchSpan, keywords.RecentPages)) + searchData.SortOrder = SortOrder.RecentlyModified; + + searchData.Search = searchSpan.ToString(); + return keyword; + + //Check if starts with keyword and if so trim the keyword + static bool StartsWithKeyword(ref ReadOnlySpan searchSpan, Keyword keyword) + { + if (searchSpan.StartsWith(keyword.Value.AsSpan(), StringComparison.Ordinal)) { - result.Score = 0; - result.AddSelectedCount = !sortByLastModified; - return ((IOneNoteItem)result.ContextData).LastModified; - }) - : source; + searchSpan = searchSpan[keyword.Length..]; + return true; + } + + return false; + } } - - public static bool StartsWithOrd(this string str, string value) => str.StartsWith(value, StringComparison.Ordinal); } } \ No newline at end of file diff --git a/Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs b/Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs index c968b6a..8d8683d 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/SearchManager.cs @@ -4,19 +4,20 @@ namespace Flow.Launcher.Plugin.OneNote.Search { public class SearchManager { - private readonly TitleSearch titleSearch; + private readonly Keywords keywords; private readonly NotebookExplorer notebookExplorer; private readonly StandardSearch standardSearch; - private readonly RecentPages recentPages; + private readonly TitleSearch titleSearch; public RootCache RootCache { get; } public SearchManager(PluginInitContext context, Settings settings, ResultCreator resultCreator) { - RootCache = new RootCache(); - titleSearch = new TitleSearch(context, settings, resultCreator, RootCache); - notebookExplorer = new NotebookExplorer(context, settings, resultCreator, RootCache); - recentPages = new RecentPages(context, settings, resultCreator, RootCache); standardSearch = new StandardSearch(context, settings, resultCreator); + titleSearch = new TitleSearch(context, settings, resultCreator); + notebookExplorer = new NotebookExplorer(context, settings, resultCreator, titleSearch, standardSearch); + + RootCache = new RootCache(); + keywords = settings.Keywords; } public List Query(Query query) @@ -25,14 +26,20 @@ public List Query(Query query) { RootCache.SetDirty(); } - string search = query.Search; - return search switch + + var searchData = new SearchData(query.Search, null, RootCache.Root.Notebooks, SortOrder.Default); + Keyword? keyword = SearchExtensions.GetKeyword(ref searchData, keywords); + + SearchBase searchBase = keyword switch { - { } when search.StartsWithOrd(titleSearch.keyword) => titleSearch.GetResults(query), - { } when search.StartsWithOrd(notebookExplorer.keyword) => notebookExplorer.GetResults(query), - { } when search.StartsWithOrd(recentPages.keyword) => recentPages.GetResults(query), - _ => standardSearch.GetResults(query), + not null when keyword == keywords.NotebookExplorer => notebookExplorer, + not null when keyword == keywords.TitleSearch => titleSearch, + _ => standardSearch, }; + + var results = searchBase.GetResults(ref searchData); + + return results.Count != 0 ? results : ResultCreator.NoMatchesFound(); } } } \ No newline at end of file diff --git a/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs b/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs index 15fcd39..3c21fc1 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs @@ -1,36 +1,47 @@ using System.Collections.Generic; using System.Linq; using LinqToOneNote; -using OneNoteApp = LinqToOneNote.OneNote; namespace Flow.Launcher.Plugin.OneNote.Search { - public class StandardSearch(PluginInitContext context, Settings settings, ResultCreator resultCreator) - : SearchBase(context, settings, resultCreator, Keyword.Empty) + public class StandardSearch(PluginInitContext context, Settings settings, ResultCreator resultCreator) : SearchBase(context, settings, resultCreator) { - //Standard search - public override List GetResults(Query query) => Filter(query.Search, null, keyword); - //Scope search - public List GetResults(Query query, IOneNoteItem? parent) => Filter(query.Search, parent, settings.Keywords.ScopedSearch); - - private List Filter(string search, IOneNoteItem? parent, Keyword keyword) + protected override List GetResultsInternal(ref SearchData searchData) { - if (search.Length == keyword.Length) - return resultCreator.SearchType("Now searching all pages", parent?.Name); + var (search, parent, _, sortOrder) = searchData; - if (!char.IsLetterOrDigit(search[keyword.Length])) + if (!char.IsLetterOrDigit(search[0])) return resultCreator.InvalidQuery(); - string currentSearch = search[keyword.Length..]; - IEnumerable pages = parent is null - ? OneNoteApp.FindPages(currentSearch) - : OneNoteApp.FindPages(currentSearch, parent); - var results = pages.Select(pg => resultCreator.CreatePageResult(pg, currentSearch)) - .ToList(); - - return results.Count != 0 ? results : ResultCreator.NoMatchesFound(); + ? LinqToOneNote.OneNote.FindPages(search) + : LinqToOneNote.OneNote.FindPages(search, parent); + + return sortOrder switch + { + SortOrder.RecentlyModified => pages.OrderByDescending(pg => pg.LastModified) + .Select(pg => resultCreator.CreateRecentItemResult(pg, false)) + .ToList(), + _ => pages.Select(pg => resultCreator.CreateOneNoteItemResult(pg, false, + string.IsNullOrWhiteSpace(search) + ? null + : context.API.FuzzySearch(search, pg.Name).MatchData)) + .ToList() + }; } + protected override List EmptySearch(ref SearchData searchData) + { + return searchData.SortOrder switch + { + SortOrder.RecentlyModified => searchData.Collection + .GetAllPages() + .FilterBySettings(settings) + .OrderByDescending(pg => pg.LastModified) + .Select(pg => resultCreator.CreateRecentItemResult(pg, false)) + .ToList(), + _ => resultCreator.SearchType("Now searching all pages", searchData.Parent?.Name), + }; + } } } \ No newline at end of file diff --git a/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs b/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs index e287166..fd5af0f 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs @@ -4,33 +4,33 @@ namespace Flow.Launcher.Plugin.OneNote.Search { - public class TitleSearch(PluginInitContext context, Settings settings, ResultCreator resultCreator, RootCache rootCache) - : SearchBase(context, settings, resultCreator, settings.Keywords.TitleSearch) + public class TitleSearch(PluginInitContext context, Settings settings, ResultCreator resultCreator) : SearchBase(context, settings, resultCreator) { - public override List GetResults(Query query) => GetResults(query.Search, null, rootCache.Root.Notebooks); - - public List GetResults(string query, IOneNoteItem? parent, IEnumerable collection) + protected override List GetResultsInternal(ref SearchData searchData) { - const string emptySearchPrefix = "Now searching by title"; - if (query.Length == keyword.Length) - //TODO return Searching by title and a Searching by last modified result with an action to auto complete last modified. - return resultCreator.SearchType(emptySearchPrefix, parent?.Name); - - var currentSearch = query[keyword.Length..]; - - var sortByLastModified = GetSortOrder(ref currentSearch, out var emptySearch); - - if(emptySearch) - return resultCreator.SearchType($"{emptySearchPrefix} ordered by last modified", parent?.Name);; - + var (search, _, collection, sortOrder) = searchData; var results = collection.Descendants() .FilterBySettings(settings) - .FuzzySearch(currentSearch, context) - .Select(x => resultCreator.CreateOneNoteItemResult(x.Item, false, x.HighlightData, x.Score)) - .OrderByLastModified(sortByLastModified) - .ToList(); + .FuzzySearch(search, context); - return results.Count != 0 ? results : ResultCreator.NoMatchesFound(); + return sortOrder switch + { + SortOrder.RecentlyModified => results.OrderByDescending(sr => sr.Item.LastModified) + .Select(sr => resultCreator.CreateRecentItemResult(sr.Item, false, sr.HighlightData)) + .ToList(), + _ => results.Select(sr => resultCreator.CreateOneNoteItemResult(sr.Item, false, sr.HighlightData, sr.Score)) + .ToList(), + }; + } + + protected override List EmptySearch(ref SearchData searchData) + { + const string emptySearchPrefix = "Now searching by title"; + return searchData.SortOrder switch + { + SortOrder.RecentlyModified => resultCreator.SearchType($"{emptySearchPrefix} ordered by last modified", searchData.Parent?.Name), + _ => resultCreator.SearchType(emptySearchPrefix, searchData.Parent?.Name), + }; } } } \ No newline at end of file From c7c9306d8fa6aee17fac2f772f106a91806d6ddf Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Mon, 13 Apr 2026 20:22:36 +0100 Subject: [PATCH 17/24] Refactor keywords --- Flow.Launcher.Plugin.OneNote/Keywords.cs | 28 +++++++++---------- .../Search/SearchExtensions.cs | 4 +-- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/Flow.Launcher.Plugin.OneNote/Keywords.cs b/Flow.Launcher.Plugin.OneNote/Keywords.cs index 908a9c0..13e61c6 100644 --- a/Flow.Launcher.Plugin.OneNote/Keywords.cs +++ b/Flow.Launcher.Plugin.OneNote/Keywords.cs @@ -7,28 +7,26 @@ namespace Flow.Launcher.Plugin.OneNote { - public class Keywords + public class Keywords : IReadOnlyList { public const string NotebookExplorerSeparator = "\\"; - public Keyword NotebookExplorer { get; set; } = new($"nb:{NotebookExplorerSeparator}"); - public Keyword RecentPages { get; set; } = new ("rp:"); - public Keyword TitleSearch { get; set; } = new ("*"); - public Keyword ScopedSearch { get; set; } = new (">"); + public Keyword NotebookExplorer { get; init; } = new($"nb:{NotebookExplorerSeparator}"); + public Keyword RecentPages { get; init; } = new ("rp:"); + public Keyword TitleSearch { get; init; } = new ("*"); + public Keyword ScopedSearch { get; init; } = new (">"); - private Keyword[]? actionKeywords; - // /// - // /// Action Keywords i.e. keywords that aren't a modifier, i.e. everything but (as of right now) - // /// - //Replace with fast reflection. - //TODO: make class IEnumerable, Dictionary or IList if recentpages is used, else use fast reflectiono - public Keyword[] ActionKeywords => actionKeywords ??= [NotebookExplorer, TitleSearch, ScopedSearch, RecentPages]; + private Keyword[]? keywords; + private Keyword[] GetKeywords() => keywords ??= [NotebookExplorer, RecentPages, TitleSearch, ScopedSearch]; + public IEnumerator GetEnumerator() => ((IEnumerable)GetKeywords()).GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetKeywords().GetEnumerator(); + public int Count => GetKeywords().Length; + public Keyword this[int index] => GetKeywords()[index]; } [JsonConverter(typeof(KeywordJsonConverter))] - public class Keyword + public class Keyword(string value) { - public Keyword(string value) => Value = value; - public string Value { get; private set; } + public string Value { get; private set; } = value; public void ChangeKeyword(string newValue) => Value = newValue; diff --git a/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs b/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs index 9c304c4..9afbff3 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs @@ -57,9 +57,9 @@ public static IEnumerable FilterBySettings(this IEnumerable source, Set Keyword? keyword = null; var searchSpan = searchData.Search.AsSpan(); - for (var i = 0; i < keywords.ActionKeywords.Length; i++) + for (var i = 0; i < keywords.Count; i++) { - Keyword key = keywords.ActionKeywords[i]; + Keyword key = keywords[i]; if (!StartsWithKeyword(ref searchSpan, key)) continue; From cdc9629232dcc377b9d29f591a33f92c79db5513 Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Mon, 13 Apr 2026 23:21:20 +0100 Subject: [PATCH 18/24] Add the to limit max number of results, remove default recent pages count --- Flow.Launcher.Plugin.OneNote/Keywords.cs | 12 +-- .../Search/SearchExtensions.cs | 9 +- .../Search/StandardSearch.cs | 5 +- .../Search/TitleSearch.cs | 4 +- Flow.Launcher.Plugin.OneNote/Settings.cs | 97 +++++++++++-------- Flow.Launcher.Plugin.OneNote/UI/Model.cs | 5 +- .../UI/ViewModels/SettingsViewModel.cs | 2 +- .../UI/Views/SettingsView.xaml | 42 ++++---- 8 files changed, 93 insertions(+), 83 deletions(-) diff --git a/Flow.Launcher.Plugin.OneNote/Keywords.cs b/Flow.Launcher.Plugin.OneNote/Keywords.cs index 13e61c6..bd805dc 100644 --- a/Flow.Launcher.Plugin.OneNote/Keywords.cs +++ b/Flow.Launcher.Plugin.OneNote/Keywords.cs @@ -7,7 +7,7 @@ namespace Flow.Launcher.Plugin.OneNote { - public class Keywords : IReadOnlyList + public class Keywords { public const string NotebookExplorerSeparator = "\\"; public Keyword NotebookExplorer { get; init; } = new($"nb:{NotebookExplorerSeparator}"); @@ -16,11 +16,8 @@ public class Keywords : IReadOnlyList public Keyword ScopedSearch { get; init; } = new (">"); private Keyword[]? keywords; - private Keyword[] GetKeywords() => keywords ??= [NotebookExplorer, RecentPages, TitleSearch, ScopedSearch]; - public IEnumerator GetEnumerator() => ((IEnumerable)GetKeywords()).GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetKeywords().GetEnumerator(); - public int Count => GetKeywords().Length; - public Keyword this[int index] => GetKeywords()[index]; + [JsonIgnore] + public Keyword[] All => keywords ??= [NotebookExplorer, RecentPages, TitleSearch, ScopedSearch]; } [JsonConverter(typeof(KeywordJsonConverter))] @@ -33,8 +30,6 @@ public class Keyword(string value) public int Length => Value.Length; public static implicit operator string(Keyword keyword) => keyword.Value; public override string ToString() => Value; - - public static Keyword Empty { get; } = new (""); } //Needed for legacy as keywords where just saved as a string @@ -46,5 +41,4 @@ public override Keyword Read(ref Utf8JsonReader reader, Type typeToConvert, Json public override void Write(Utf8JsonWriter writer, Keyword value, JsonSerializerOptions options) => JsonSerializer.Serialize(writer, value.Value, options); } - } \ No newline at end of file diff --git a/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs b/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs index 9afbff3..6cd485e 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs @@ -46,6 +46,11 @@ public static IEnumerable FilterBySettings(this IEnumerable source, Set }); } + public static IEnumerable Take(this IEnumerable source, Settings settings) => + settings.UseMaxResults + ? source.Take(settings.MaxResults) + : source; + /// /// Gets the keyword from the search, and removes it from the search string. Also gets the sort order /// @@ -57,9 +62,9 @@ public static IEnumerable FilterBySettings(this IEnumerable source, Set Keyword? keyword = null; var searchSpan = searchData.Search.AsSpan(); - for (var i = 0; i < keywords.Count; i++) + for (var i = 0; i < keywords.All.Length; i++) { - Keyword key = keywords[i]; + Keyword key = keywords.All[i]; if (!StartsWithKeyword(ref searchSpan, key)) continue; diff --git a/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs b/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs index 3c21fc1..f8e0ed7 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/StandardSearch.cs @@ -20,9 +20,11 @@ protected override List GetResultsInternal(ref SearchData searchData) return sortOrder switch { SortOrder.RecentlyModified => pages.OrderByDescending(pg => pg.LastModified) + .Take(settings) .Select(pg => resultCreator.CreateRecentItemResult(pg, false)) .ToList(), - _ => pages.Select(pg => resultCreator.CreateOneNoteItemResult(pg, false, + _ => pages.Take(settings) + .Select(pg => resultCreator.CreateOneNoteItemResult(pg, false, string.IsNullOrWhiteSpace(search) ? null : context.API.FuzzySearch(search, pg.Name).MatchData)) @@ -38,6 +40,7 @@ protected override List EmptySearch(ref SearchData searchData) .GetAllPages() .FilterBySettings(settings) .OrderByDescending(pg => pg.LastModified) + .Take(settings) .Select(pg => resultCreator.CreateRecentItemResult(pg, false)) .ToList(), _ => resultCreator.SearchType("Now searching all pages", searchData.Parent?.Name), diff --git a/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs b/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs index fd5af0f..1f5ae54 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/TitleSearch.cs @@ -16,9 +16,11 @@ protected override List GetResultsInternal(ref SearchData searchData) return sortOrder switch { SortOrder.RecentlyModified => results.OrderByDescending(sr => sr.Item.LastModified) + .Take(settings) .Select(sr => resultCreator.CreateRecentItemResult(sr.Item, false, sr.HighlightData)) .ToList(), - _ => results.Select(sr => resultCreator.CreateOneNoteItemResult(sr.Item, false, sr.HighlightData, sr.Score)) + _ => results.Take(settings) + .Select(sr => resultCreator.CreateOneNoteItemResult(sr.Item, false, sr.HighlightData, sr.Score)) .ToList(), }; } diff --git a/Flow.Launcher.Plugin.OneNote/Settings.cs b/Flow.Launcher.Plugin.OneNote/Settings.cs index 109321d..5391ee5 100644 --- a/Flow.Launcher.Plugin.OneNote/Settings.cs +++ b/Flow.Launcher.Plugin.OneNote/Settings.cs @@ -2,46 +2,57 @@ namespace Flow.Launcher.Plugin.OneNote { - public class Settings : UI.Model - { - private bool showUnread = true; - private int defaultRecentsCount = 5; - private bool showRecycleBin = true; - private bool showEncrypted; - private bool createColoredIcons = true; - private IconTheme iconTheme = IconTheme.Color; - public Keywords Keywords { get; init; } = new Keywords(); - - public bool ShowRecycleBin - { - get => showRecycleBin; - set => SetProperty(ref showRecycleBin, value); - } - public bool ShowUnread - { - get => showUnread; - set => SetProperty(ref showUnread, value); - } - public int DefaultRecentsCount - { - get => defaultRecentsCount; - set => SetProperty(ref defaultRecentsCount, value); - } - public bool ShowEncrypted - { - get => showEncrypted; - set => SetProperty(ref showEncrypted, value); - } - public bool CreateColoredIcons - { - get => createColoredIcons; - set => SetProperty(ref createColoredIcons, value); - } - - public IconTheme IconTheme - { - get => iconTheme; - set => SetProperty(ref iconTheme, value); - } - } -} + public class Settings : UI.Model + { + private bool showUnread = true; + private bool showRecycleBin = true; + private bool showEncrypted; + private bool createColoredIcons = true; + private int maxResults = 30; + private bool useMaxResults = true; + private IconTheme iconTheme = IconTheme.Color; + public Keywords Keywords { get; init; } = new Keywords(); + + public bool ShowRecycleBin + { + get => showRecycleBin; + set => SetProperty(ref showRecycleBin, value); + } + + public bool ShowUnread + { + get => showUnread; + set => SetProperty(ref showUnread, value); + } + + public int MaxResults + { + get => maxResults; + set => SetProperty(ref maxResults, value); + } + + public bool UseMaxResults + { + get => useMaxResults; + set => SetProperty(ref useMaxResults, value); + } + + public bool ShowEncrypted + { + get => showEncrypted; + set => SetProperty(ref showEncrypted, value); + } + + public bool CreateColoredIcons + { + get => createColoredIcons; + set => SetProperty(ref createColoredIcons, value); + } + + public IconTheme IconTheme + { + get => iconTheme; + set => SetProperty(ref iconTheme, value); + } + } +} \ No newline at end of file diff --git a/Flow.Launcher.Plugin.OneNote/UI/Model.cs b/Flow.Launcher.Plugin.OneNote/UI/Model.cs index 71699e8..5379f54 100644 --- a/Flow.Launcher.Plugin.OneNote/UI/Model.cs +++ b/Flow.Launcher.Plugin.OneNote/UI/Model.cs @@ -1,4 +1,5 @@ -using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System.Runtime.CompilerServices; namespace Flow.Launcher.Plugin.OneNote.UI { @@ -6,7 +7,7 @@ public class Model : BaseModel { protected bool SetProperty(ref T field, T newValue, [CallerMemberName] string? propertyName = null) { - if (Equals(field, newValue)) + if (EqualityComparer.Default.Equals(field, newValue)) return false; field = newValue; diff --git a/Flow.Launcher.Plugin.OneNote/UI/ViewModels/SettingsViewModel.cs b/Flow.Launcher.Plugin.OneNote/UI/ViewModels/SettingsViewModel.cs index 91e153a..5b305c5 100644 --- a/Flow.Launcher.Plugin.OneNote/UI/ViewModels/SettingsViewModel.cs +++ b/Flow.Launcher.Plugin.OneNote/UI/ViewModels/SettingsViewModel.cs @@ -21,6 +21,7 @@ public SettingsViewModel(PluginInitContext context, Settings settings, IconProvi Keywords = settings.Keywords //Order is the order they are written in Keywords.cs .GetType() .GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(p => p.PropertyType == typeof(Keyword)) .Select(p => new KeywordViewModel(p.Name.Humanize(LetterCasing.Title), (Keyword)p.GetValue(settings.Keywords)!)) .ToArray(); IconThemes = Enum.GetValues() @@ -61,7 +62,6 @@ public SettingsViewModel(PluginInitContext context, Settings settings, IconProvi public Settings Settings { get; } public KeywordViewModel[] Keywords { get; } public KeywordViewModel NotebookExplorerKeyword => Keywords[0]; - public KeywordViewModel RecentPagesKeyword => Keywords[1]; public IconThemeViewModel[] IconThemes { get; } public string CachedIconsFileSize => iconProvider.GeneratedImagesDirectoryInfo.EnumerateFiles() .Select(file => file.Length) diff --git a/Flow.Launcher.Plugin.OneNote/UI/Views/SettingsView.xaml b/Flow.Launcher.Plugin.OneNote/UI/Views/SettingsView.xaml index 0b7745d..fb560e1 100644 --- a/Flow.Launcher.Plugin.OneNote/UI/Views/SettingsView.xaml +++ b/Flow.Launcher.Plugin.OneNote/UI/Views/SettingsView.xaml @@ -19,15 +19,6 @@ - - - 1 - 20 - - @@ -164,25 +155,28 @@ - - + + - - + + - - + + + + + From da4ff24ef0d728876e8e053981e538bfc2855533 Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Tue, 14 Apr 2026 19:56:42 +0100 Subject: [PATCH 19/24] Renamed `RecentPages` to `SortByLastModified` --- Flow.Launcher.Plugin.OneNote/Keywords.cs | 5 +++-- Flow.Launcher.Plugin.OneNote/ResultCreator.cs | 6 +++--- Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs | 2 +- Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher.Plugin.OneNote/Keywords.cs b/Flow.Launcher.Plugin.OneNote/Keywords.cs index bd805dc..199c3ec 100644 --- a/Flow.Launcher.Plugin.OneNote/Keywords.cs +++ b/Flow.Launcher.Plugin.OneNote/Keywords.cs @@ -11,13 +11,14 @@ public class Keywords { public const string NotebookExplorerSeparator = "\\"; public Keyword NotebookExplorer { get; init; } = new($"nb:{NotebookExplorerSeparator}"); - public Keyword RecentPages { get; init; } = new ("rp:"); + [JsonPropertyName("RecentPages")] + public Keyword SortByLastModified { get; init; } = new ("rp:"); public Keyword TitleSearch { get; init; } = new ("*"); public Keyword ScopedSearch { get; init; } = new (">"); private Keyword[]? keywords; [JsonIgnore] - public Keyword[] All => keywords ??= [NotebookExplorer, RecentPages, TitleSearch, ScopedSearch]; + public Keyword[] All => keywords ??= [NotebookExplorer, SortByLastModified, TitleSearch, ScopedSearch]; } [JsonConverter(typeof(KeywordJsonConverter))] diff --git a/Flow.Launcher.Plugin.OneNote/ResultCreator.cs b/Flow.Launcher.Plugin.OneNote/ResultCreator.cs index 3190d31..e6f3328 100644 --- a/Flow.Launcher.Plugin.OneNote/ResultCreator.cs +++ b/Flow.Launcher.Plugin.OneNote/ResultCreator.cs @@ -84,14 +84,14 @@ public List EmptyQuery() new Result { Title = "See recent pages", - SubTitle = $"Type \"{settings.Keywords.RecentPages}\" or select this option to see recently modified pages", - AutoCompleteText = $"{ActionKeyword} {settings.Keywords.RecentPages}", + SubTitle = $"Type \"{settings.Keywords.SortByLastModified}\" or select this option to see recently modified pages", + AutoCompleteText = $"{ActionKeyword} {settings.Keywords.SortByLastModified}", IcoPath = iconProvider.Recent, AddSelectedCount = false, Score = -1000, Action = _ => { - context.API.ChangeQuery($"{ActionKeyword} {settings.Keywords.RecentPages}", true); + context.API.ChangeQuery($"{ActionKeyword} {settings.Keywords.SortByLastModified}", true); return false; }, }, diff --git a/Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs b/Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs index 880c3e1..2e8674a 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/NotebookExplorer.cs @@ -35,7 +35,7 @@ protected override List GetResultsInternal(ref SearchData searchData) Result result = resultCreator.CreateOneNoteItemResult(parent, false, score: Result.MaxScore); result.Title = $"Open \"{parent.Name}\" in OneNote"; - result.SubTitle = $"Use \'{keywords.ScopedSearch}\' to search this item. Use \'{keywords.TitleSearch}\' to search by title in this item. Use \'{keywords.RecentPages}\' to sort by recently modified"; + result.SubTitle = $"Use \'{keywords.ScopedSearch}\' to search this item. Use \'{keywords.TitleSearch}\' to search by title in this item. Use \'{keywords.SortByLastModified}\' to sort by recently modified"; results.Add(result); return results; } diff --git a/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs b/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs index 6cd485e..08ee964 100644 --- a/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs +++ b/Flow.Launcher.Plugin.OneNote/Search/SearchExtensions.cs @@ -72,7 +72,7 @@ public static IEnumerable Take(this IEnumerable source, Settings settin break; } - if (keyword == keywords.RecentPages || StartsWithKeyword(ref searchSpan, keywords.RecentPages)) + if (keyword == keywords.SortByLastModified || StartsWithKeyword(ref searchSpan, keywords.SortByLastModified)) searchData.SortOrder = SortOrder.RecentlyModified; searchData.Search = searchSpan.ToString(); From 039771fceb618970da3a098e26a2c416f3b3dcaa Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Sat, 25 Apr 2026 16:30:53 +0100 Subject: [PATCH 20/24] Update changelog, readme and plugin.json --- Changelog.md | 15 +++++++- Flow.Launcher.Plugin.OneNote/plugin.json | 2 +- Readme.md | 45 +++++++++++++++--------- 3 files changed, 43 insertions(+), 19 deletions(-) diff --git a/Changelog.md b/Changelog.md index aa11fd7..8413d29 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,11 +1,24 @@ # Changelog +## 3.1.0 + +- :star: **Recent pages** is dead, long live **sort by last modified** ([#32](https://github.com/Odotocodot/Flow.Launcher.Plugin.OneNote/issues/32)) + - Using the **sort by last modified** keyword, as the name implies, allows you to sort results by last modified. But unlike **recent pages** you can now use it in multiple places, i.e. with **title search**, **scoped search** and **notebook explorer**. Note it must be placed after the aforementioned keywords. + - Examples:\ + (`#` = **sort by last modified** keyword, `*` = **title search** keyword, `nb:\` = **notebook explorer** keyword, `>` = **scoped search** keyword) + - ```on: #{your search query}``` + - ```on: *#{your search query}``` + - ```on: nb:\PathToItem\#{your search query}``` + - ```on: nb:\PathToItem\>#{your search query}``` + - ```on: nb:\PathToItem\*#{your search query}``` + + ## 3.0.1 - 2026-03-13 - Fix crash on quick note with empty title (Updated to `LinqToOneNote-2.1.1`) - Fix incorrect **scope search** check - Cache OneNote hierarchy when applicable - - This brings the performance improvement for Notebook Explorer from last version to **recent pages** and **title search** + - This brings the performance improvement from **notebook explorer** from last version to **recent pages** and **title search** ## 3.0.0 - 2026-03-04 diff --git a/Flow.Launcher.Plugin.OneNote/plugin.json b/Flow.Launcher.Plugin.OneNote/plugin.json index c4fbc05..e3fadcd 100644 --- a/Flow.Launcher.Plugin.OneNote/plugin.json +++ b/Flow.Launcher.Plugin.OneNote/plugin.json @@ -4,7 +4,7 @@ "Name": "OneNote", "Description": "Search and create your OneNote notes", "Author": "Odotocodot", - "Version": "3.0.1", + "Version": "3.1.0", "Language": "csharp", "Website": "https://github.com/Odotocodot/Flow.Launcher.Plugin.OneNote", "IcoPath": "Images/logo.png", diff --git a/Readme.md b/Readme.md index 47a3092..47afea8 100644 --- a/Readme.md +++ b/Readme.md @@ -24,11 +24,11 @@ A [OneNote](https://www.microsoft.com/en-gb/microsoft-365/onenote/digital-note-t - [Installation](#installation) - [Features](#features) - [At a Glance](#at-a-glance) - - [New with Version 2.1.0](#new-with-version-210) - [Default Search](#default-search) - [Notebook Explorer](#notebook-explorer) - [Create New Items](#create-new-items) - - [Recent Pages](#recent-pages) + - [Create Pages](#create-pages) + - [Sort by Last Modified](#sort-by-last-modified) - [Scoped Search](#scoped-search) - [Title Search](#title-search) - [Settings](#settings) @@ -48,7 +48,7 @@ pm install OneNote > [!IMPORTANT] > > - [Versions `2.0+`](Changelog.md#200---2023-10-05) requires Flow Launcher version `1.16+`. For earlier versions see [releases](https://github.com/Odotocodot/Flow.Launcher.Plugin.OneNote/releases). -> - [Versions `3.0+`](Changelog.md#300---2026-03-04) requires Flow Launcher version `2.1+`. +> - [Versions `3.0+`](Changelog.md#300---2026-03-04) requires Flow Launcher version `2.1+`. > - This plugin is local only! It requires an installation of OneNote on you system! > - Some features require Windows/Microsoft Search to be enabled on your system. See more info [here](#additional-information). @@ -60,23 +60,21 @@ pm install OneNote | -------------------------- | --------------------------------------- | ---------------------------- | | ` on {your search query} ` | [Default Search](#default-search) | Search OneNote pages | | ` on nb:\ ` | [Notebook Explorer](#notebook-explorer) | Navigate notebooks hierarchy | -| ` on rp: ` | [Recent Pages](#recent-pages) | View recently modified pages | #### Modifiers -| Keyword | Name | Notes | -| ------- | ------------------------------- | -------------------------------------------- | -| ` > ` | [Scoped Search](#scoped-search) | Search only within a specific hierarchy item | -| ` * ` | [Title Search](#title-search) | Search only the title of hierarchy items | +| Keyword | Name | Notes | +| ------- | ----------------------------------------------- | -------------------------------------------- | +| ` > ` | [Scoped Search](#scoped-search) | Search only within a specific hierarchy item | +| ` * ` | [Title Search](#title-search) | Search only the title of hierarchy items | +| ` # ` | [Sort by Last Modified](#sort-by-last-modified) | Sort results by last modified | > [!NOTE] > Hierarchy items are notebooks, section groups, sections and pages. -### New with Version 2.1.0 - -- :star: [New page preview panel](#create-pages) :star: for quickly creating pages with text! -- Added icons for different themes. The themes available are color, light, dark and one that matches Flow Launcher's current theme. Change it in the settings! +> [!NOTE] +> Keywords are customisable in the [settings](#settings). ### Default Search @@ -140,17 +138,30 @@ This preview panel is available on all *create a page* results in the plugin and ![create pages gif](doc/create_page.gif) -### Recent Pages +### Sort by Last Modified ``` -on rp: +on #{your search query} ``` -Displays your recently modified OneNote pages. +Displays your results sorted by last modified. -Add a number after `` rp: `` to display that number of recent pages. E.g. the full query ``on rp:10`` will show the 10 most recently modified pages. +Can be used with [default search](#default-search), [notebook explorer](#notebook-explorer), [title search](#title-search) and [scoped search](#scoped-search). -![recent pages gif](doc/recent_pages.gif) +
+

Examples

+ +- ```on: #{your search query}``` +- ```on: *#{your search query}``` +- ```on: nb:\PathToItem\#{your search query}``` +- ```on: nb:\PathToItem\>#{your search query}``` +- ```on: nb:\PathToItem\*#{your search query}``` + +
+ + +> [!IMPORTANT] +> The sort by last modified keyword must be placed after other keywords but before your search. ### Scoped Search From d541fa7553279cfc27831547ed09b00d61f913b0 Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Sat, 25 Apr 2026 19:32:23 +0100 Subject: [PATCH 21/24] Added setting to allow users to always open items in a new OneNote window. Also refactor `IconGeneratorInfo`. Closes #40 --- Changelog.md | 4 +- .../Icons/IconGeneratorInfo.cs | 35 +++++----- Flow.Launcher.Plugin.OneNote/ResultCreator.cs | 52 ++++++++------- Flow.Launcher.Plugin.OneNote/Settings.cs | 7 ++ .../UI/ViewModels/NewOneNotePageViewModel.cs | 7 +- .../Views/NewOneNotePagePreviewPanel.xaml.cs | 4 +- .../UI/Views/SettingsView.xaml | 66 ++++++++++++------- 7 files changed, 100 insertions(+), 75 deletions(-) diff --git a/Changelog.md b/Changelog.md index 8413d29..646b663 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,7 +2,7 @@ ## 3.1.0 -- :star: **Recent pages** is dead, long live **sort by last modified** ([#32](https://github.com/Odotocodot/Flow.Launcher.Plugin.OneNote/issues/32)) +- :star: **Recent pages** is dead, long live **sort by last modified**. ([#32](https://github.com/Odotocodot/Flow.Launcher.Plugin.OneNote/issues/32)) - Using the **sort by last modified** keyword, as the name implies, allows you to sort results by last modified. But unlike **recent pages** you can now use it in multiple places, i.e. with **title search**, **scoped search** and **notebook explorer**. Note it must be placed after the aforementioned keywords. - Examples:\ (`#` = **sort by last modified** keyword, `*` = **title search** keyword, `nb:\` = **notebook explorer** keyword, `>` = **scoped search** keyword) @@ -11,7 +11,7 @@ - ```on: nb:\PathToItem\#{your search query}``` - ```on: nb:\PathToItem\>#{your search query}``` - ```on: nb:\PathToItem\*#{your search query}``` - +- Added a setting to allow users to always open items in a new OneNote window. ([#40](https://github.com/Odotocodot/Flow.Launcher.Plugin.OneNote/issues/40)) ## 3.0.1 - 2026-03-13 diff --git a/Flow.Launcher.Plugin.OneNote/Icons/IconGeneratorInfo.cs b/Flow.Launcher.Plugin.OneNote/Icons/IconGeneratorInfo.cs index fdb9d41..30013e5 100644 --- a/Flow.Launcher.Plugin.OneNote/Icons/IconGeneratorInfo.cs +++ b/Flow.Launcher.Plugin.OneNote/Icons/IconGeneratorInfo.cs @@ -3,30 +3,27 @@ namespace Flow.Launcher.Plugin.OneNote.Icons { - public struct IconGeneratorInfo + public readonly struct IconGeneratorInfo { public readonly string prefix = string.Empty; public readonly Color? color; - - public IconGeneratorInfo(IOneNoteItem item) + + private IconGeneratorInfo(string prefix, Color? color = null) + { + this.prefix = prefix; + this.color = color; + } + + public static IconGeneratorInfo Create(IOneNoteItem item) { - switch (item) + return item switch { - case Notebook n: - prefix = IconConstants.Notebook; - color = n.Color; - break; - case SectionGroup sg: - prefix = sg.IsRecycleBin ? IconConstants.RecycleBin : IconConstants.SectionGroup; - break; - case Section s: - prefix = IconConstants.Section; - color = s.Color; - break; - case Page: - prefix = IconConstants.Page; - break; - } + Notebook n => new (IconConstants.Notebook, n.Color), + SectionGroup sg => new (sg.IsRecycleBin ? IconConstants.RecycleBin : IconConstants.SectionGroup), + Section s => new (IconConstants.Section, s.Color), + Page => new (IconConstants.Page), + _ => new (), + }; } } } \ No newline at end of file diff --git a/Flow.Launcher.Plugin.OneNote/ResultCreator.cs b/Flow.Launcher.Plugin.OneNote/ResultCreator.cs index e6f3328..856acc3 100644 --- a/Flow.Launcher.Plugin.OneNote/ResultCreator.cs +++ b/Flow.Launcher.Plugin.OneNote/ResultCreator.cs @@ -11,22 +11,12 @@ namespace Flow.Launcher.Plugin.OneNote { - public class ResultCreator + public class ResultCreator(PluginInitContext context, Settings settings, IconProvider iconProvider) { - private readonly PluginInitContext context; - private readonly Settings settings; - private readonly IconProvider iconProvider; - private const string PathSeparator = " > "; private const string BulletPoint = "\u2022 "; private const string TrianglePoint = "\u2023 "; private string ActionKeyword => context.CurrentPluginMetadata.ActionKeyword; - public ResultCreator(PluginInitContext context, Settings settings, IconProvider iconProvider) - { - this.settings = settings; - this.iconProvider = iconProvider; - this.context = context; - } private static string GetNicePath(IOneNoteItem item, string separator = PathSeparator) => item.GetRelativePath(false, separator); @@ -104,7 +94,7 @@ public List EmptyQuery() PreviewPanel = GetNewPagePreviewPanel(null, null), Action = _ => { - OneNoteApp.CreateQuickNote(OpenMode.ExistingOrNewWindow); + OneNoteApp.CreateQuickNote(settings.AlwaysOpenInNewWindow ? OpenMode.NewWindow : OpenMode.ExistingOrNewWindow); WindowHelper.FocusOneNote(); return true; }, @@ -142,7 +132,7 @@ public Result CreateOneNoteItemResult(IOneNoteItem item, bool actionIsAutoComple var toolTip = string.Empty; var subTitle = GetNicePath(item); var autoCompleteText = GetAutoCompleteText(item); - var iconInfo = new IconGeneratorInfo(item); + var iconInfo = IconGeneratorInfo.Create(item); switch (item) { @@ -213,7 +203,7 @@ public Result CreateOneNoteItemResult(IOneNoteItem item, bool actionIsAutoComple await Task.Run(() => { item.Sync(); - item.Open(); + OneNoteApp.Open(item, settings.AlwaysOpenInNewWindow); }); WindowHelper.FocusOneNote(); return true; @@ -233,7 +223,7 @@ public Result CreateRecentItemResult(IOneNoteItem item, bool actionIsAutoComplet return result; } - //When name can have invalid chars + //When new name can have invalid chars private Result CreateNewItemResult(string newName, TParent? parent, string iconPath, Func createFunc) where TNew : IOneNoteItem, INameInvalidCharacters where TParent : IOneNoteItem @@ -261,7 +251,10 @@ private Result CreateNewItemResult(string newName, TParent? paren return false; bool showOneNote = !c.SpecialKeyState.CtrlPressed; - createFunc(parent, newName, showOneNote ? OpenMode.ExistingOrNewWindow : OpenMode.None); + bool newWindow = settings.AlwaysOpenInNewWindow; + + OpenMode openMode = showOneNote ? newWindow ? OpenMode.NewWindow : OpenMode.ExistingOrNewWindow : OpenMode.None; + createFunc(parent, newName, openMode); context.API.ReQuery(); @@ -291,18 +284,27 @@ public List ContextMenu(Result selectedResult) var results = new List(); if (selectedResult.ContextData is IOneNoteItem item) { - var result = CreateOneNoteItemResult(item, false); - result.Title = $"Open and sync \"{item.Name}\""; - result.SubTitle = string.Empty; - result.Score = 30; - result.AddSelectedCount = false; - result.ContextData = null; - results.Add(result); + Result.IconDelegate icon = iconProvider.GetIcon(IconGeneratorInfo.Create(item)); + results.Add(new Result + { + Title = $"Open and sync \"{item.Name}\"", + Icon = icon, + Score = 30, + AddSelectedCount = false, + Action = _ => + { + OneNoteApp.Open(item); + OneNoteApp.SyncItem(item); + WindowHelper.FocusOneNote(); + return true; + } + }); + results.Add(new Result { Title = $"Open \"{item.Name}\" in new OneNote window", - Icon = result.Icon, + Icon = icon, Score = 20, AddSelectedCount = false, Action = _ => @@ -381,7 +383,7 @@ Result EmptyCollectionResult(string title, string iconPath, string? subTitle = n } private Lazy GetNewPagePreviewPanel(Section? section, string? pageTitle) - => new(() => new NewOneNotePagePreviewPanel(context, section, pageTitle)); + => new(() => new NewOneNotePagePreviewPanel(context, settings, section, pageTitle)); public static List NoMatchesFound() { diff --git a/Flow.Launcher.Plugin.OneNote/Settings.cs b/Flow.Launcher.Plugin.OneNote/Settings.cs index 5391ee5..455cbe0 100644 --- a/Flow.Launcher.Plugin.OneNote/Settings.cs +++ b/Flow.Launcher.Plugin.OneNote/Settings.cs @@ -10,6 +10,7 @@ public class Settings : UI.Model private bool createColoredIcons = true; private int maxResults = 30; private bool useMaxResults = true; + private bool alwaysOpenInNewWindow = false; private IconTheme iconTheme = IconTheme.Color; public Keywords Keywords { get; init; } = new Keywords(); @@ -54,5 +55,11 @@ public IconTheme IconTheme get => iconTheme; set => SetProperty(ref iconTheme, value); } + + public bool AlwaysOpenInNewWindow + { + get => alwaysOpenInNewWindow; + set => alwaysOpenInNewWindow = value; + } } } \ No newline at end of file diff --git a/Flow.Launcher.Plugin.OneNote/UI/ViewModels/NewOneNotePageViewModel.cs b/Flow.Launcher.Plugin.OneNote/UI/ViewModels/NewOneNotePageViewModel.cs index b70c486..9c6adfb 100644 --- a/Flow.Launcher.Plugin.OneNote/UI/ViewModels/NewOneNotePageViewModel.cs +++ b/Flow.Launcher.Plugin.OneNote/UI/ViewModels/NewOneNotePageViewModel.cs @@ -12,10 +12,12 @@ public class NewOneNotePageViewModel : Model private string pageContent = string.Empty; private readonly Section? section; private readonly PluginInitContext context; + private readonly Settings settings; - public NewOneNotePageViewModel(PluginInitContext context, Section? section, string? pageTitle) + public NewOneNotePageViewModel(PluginInitContext context, Settings settings, Section? section, string? pageTitle) { this.context = context; + this.settings = settings; this.section = section; PageTitle = pageTitle; CreateCommand = new RelayCommand(() => CreatePage(false)); @@ -33,6 +35,7 @@ private void CreatePage(bool openImmediately) { page = OneNoteApp.CreatePage(section, PageTitle); } + var xmlWrap = $""" @@ -52,7 +55,7 @@ private void CreatePage(bool openImmediately) context.API.ReQuery(); if (openImmediately) { - page.Open(); + OneNoteApp.Open(page, settings.AlwaysOpenInNewWindow); context.API.HideMainWindow(); WindowHelper.FocusOneNote(); } diff --git a/Flow.Launcher.Plugin.OneNote/UI/Views/NewOneNotePagePreviewPanel.xaml.cs b/Flow.Launcher.Plugin.OneNote/UI/Views/NewOneNotePagePreviewPanel.xaml.cs index 4d000ed..a36608a 100644 --- a/Flow.Launcher.Plugin.OneNote/UI/Views/NewOneNotePagePreviewPanel.xaml.cs +++ b/Flow.Launcher.Plugin.OneNote/UI/Views/NewOneNotePagePreviewPanel.xaml.cs @@ -8,9 +8,9 @@ namespace Flow.Launcher.Plugin.OneNote.UI.Views { public partial class NewOneNotePagePreviewPanel : UserControl { - public NewOneNotePagePreviewPanel(PluginInitContext context, Section? section, string? pageTitle) + public NewOneNotePagePreviewPanel(PluginInitContext context, Settings settings, Section? section, string? pageTitle) { - DataContext = new NewOneNotePageViewModel(context, section, pageTitle); + DataContext = new NewOneNotePageViewModel(context, settings, section, pageTitle); InitializeComponent(); } private void NewOneNotePagePreviewPanel_OnLoaded(object sender, RoutedEventArgs e) diff --git a/Flow.Launcher.Plugin.OneNote/UI/Views/SettingsView.xaml b/Flow.Launcher.Plugin.OneNote/UI/Views/SettingsView.xaml index fb560e1..aa1e65f 100644 --- a/Flow.Launcher.Plugin.OneNote/UI/Views/SettingsView.xaml +++ b/Flow.Launcher.Plugin.OneNote/UI/Views/SettingsView.xaml @@ -76,6 +76,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -155,31 +196,6 @@ - - - - - - - - - - - - - - - - From f9a24a2d45395a5354e80b93d99ea33dbb49d40b Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Sat, 25 Apr 2026 19:32:57 +0100 Subject: [PATCH 22/24] Tweaks and cleanup --- .gitignore | 1 + Flow.Launcher.Plugin.OneNote/Keywords.cs | 2 -- Readme.md | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 0085abe..a7e41b3 100644 --- a/.gitignore +++ b/.gitignore @@ -354,3 +354,4 @@ MigrationBackup/ #VSCode .vscode/ .idea/ +*.lscache diff --git a/Flow.Launcher.Plugin.OneNote/Keywords.cs b/Flow.Launcher.Plugin.OneNote/Keywords.cs index 199c3ec..f92b5b5 100644 --- a/Flow.Launcher.Plugin.OneNote/Keywords.cs +++ b/Flow.Launcher.Plugin.OneNote/Keywords.cs @@ -1,6 +1,4 @@ using System; -using System.Collections; -using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; diff --git a/Readme.md b/Readme.md index 47afea8..c3d9163 100644 --- a/Readme.md +++ b/Readme.md @@ -3,7 +3,7 @@ - +

OneNote for Flow Launcher

@@ -235,7 +235,7 @@ All the keywords used can be changed according to user preference. ## Changelog -See [here](Changelog.md) for the full list of changes. +See [Changelog.md](Changelog.md) for the full list of changes. ## Additional Information From cd3b09cb78e835fa0e94a4938673488447589223 Mon Sep 17 00:00:00 2001 From: Odotocodot <48138990+Odotocodot@users.noreply.github.com> Date: Sat, 25 Apr 2026 19:33:21 +0100 Subject: [PATCH 23/24] Update logo and doc images --- Flow.Launcher.Plugin.OneNote/Images/logo.png | Bin 3907 -> 35704 bytes doc/keywords.png | Bin 10077 -> 9357 bytes doc/onenote.png | Bin 5825 -> 0 bytes doc/settings.png | Bin 43495 -> 60015 bytes 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/onenote.png diff --git a/Flow.Launcher.Plugin.OneNote/Images/logo.png b/Flow.Launcher.Plugin.OneNote/Images/logo.png index ea9fb287bf0470fb1799187f2f6081becb3e12f3..db3e3876dbbcd4391b62775becaaca9b28e1185f 100644 GIT binary patch literal 35704 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 diff --git a/doc/keywords.png b/doc/keywords.png index 1231593176d2fd27c1b30d68960d274fa2ae7bd5..1d59a9dfbd81f72aaa0ab04fed163ccc32e8a0fe 100644 GIT binary patch literal 9357 zcma)i2UJttwr(g&ks3fmYGBiQuL=Z^gsM`M-Vqdm0MeyM?;%L<9jVfLlNvz~5Tqj# zgn)p8G!eMb|NQ5kckX%bzA=)_tXbFEYtL`)HOG#+tD{a%dXp3c0+GXEs`o*lt1u7< zuZNfb$5{r`O5+Hg`+apNsCtYEiyM%*!i?NOpzEB!zj*Nbz&8*G-|DWGz8W(#^X28` z`T6;;U%!6-{CRqMdUA4de0+R#baZ%l_~XZqgM)+p{r&IXzkmDoZEtUHcXxMZXXoqJ zuiM+(TU%RLEOv8qb7N!U%a+2W{W^HZl^XJd2tE-ka-t_kNc6WDob#--ic6M}hw70jnwY9ajwzjmi zG&eUlH8nLhHa0Xg)YsS7)z#J3*4EV2R99D5RaI41R#sG0c((48mzO_l-g@=wRaseC zX=$l@!$xs&v3u?3qN1Y0!oq@rg8cmayu7^J+}xa;oUE*@%*@P;jEwa3^wiYUl$4a@ zf++^ z^yyP4C#NS*o;-g1*wNAP(W6K9_V#vmcDA;*Ha0d^R#ujlmKGKk=H})mCMFLbK78=t zfq{X6zP`Syp{MGhiHV7dii(Jc2n!1f2?+@b3JM4a@bmNY z@$vEU^78QTaC38WadB~SasmLr!NI}K&JKY<*x1-uSy@?FSk?&p^gtlU09;jB-+TV+ z$J)oV_h^T#bekUDpjqTY(uUJrQ=a7$MoPb~)1*Ga^CzHgS5UeCgsk4{I3DdpyW8GM zN}c}L#>)QN*;~EK)gd2z5a?SZDd?9V1WySDq5^|L2|*A%+~=R8Ka9Ude~WRWe~SNT z{>$huQx>)MT4U2dme6fqpM|C>ByCKom*S|SMPQ1F-m*u}!A1iVNfd4SCJQVKSf)NN?*B~S@+H+YoSyR3G zbJYMIbn?)*LHry5%nY)UbN9av47u$I@Wl{>7OkC5dF|zP(xT$0WO%weqGx5tjyFPN z1TXs|WG;G_nL8xh!Vydx$M&9!s?vj!YTy|yv!@SX)D0@diA)NFR|qnvj_QOWi#xp? z7FC3bVU%@5x>Z6AQ!2i^b$8#o^AUGnR&|JXsV+>TmFMP8{3$cgwr;+weWPb%szXgB zj;ut0WGMT}?U(nq^Embw{bhHNA13TfNh(@bx2lA;Iv|AfQ$NhYRHquw@ZnGQlO5lF z)N|8@8Mk^NwXAwt*eMESEq7y^@Jc2jB(F;z0U0H6iojT+o~#IeQobq8e(rpZMHqmM zA+jNhy3(f%UQwyT1i`}q)w0wk3GH@Dq>Q?%c|O<2b4V1-9Wp!>*%0wrIy0EHaLiuP%Ge$)s*?ocsUBM7q5;j6c%HUpQfCP)AF+H~ z3(*x}4NNM=mg>3<8?5v%s2TO+^X4Zk)(4QGw;2d7`I#{pC3YZ`hx#Ps)TTG2`MSzv zZ**mk;aMJTG6Cmv8g36#o4PUyaa2W^iNTO-lE)) z)+@q`<`N~WDw81mw`BIseNm{TbNqxEY5PMinD&$y2;RnlMP})ILfdml@38rFo>FCI z`mtUId)?k2({Phc+Bpz$o|qxVQBFl`(`!V)S=qr2W&9Yj*yjek*12CTbBEA#BaC{l zky_fns95PyFK?hoLop`*Sb#om0Y=cFfMqYF?doKTduRh{aeseBYVD^i4XN4A%k}Q7 z2U>iD4aud$<lHF;=8q0z&wG9TFIt zj!4=u({jqfQhk&V4EgQJ?(S1EWxY$*RxFI>sYDF2aI;hWKl2Q!`aeS zbl3+fP|46=&M}9iCU@drt6h)rcsxsf>~<1Z13J*km8oT+Q3 zqv&|_M-1jY?=_keu6Hc?MaCGI*i9=M4VJR6=l!1Q!B5+>jeTK1ugZD-8-s|nW2*ZaRH$C1 zHMV>T_J@n1K_PmnFOul-y_pGpv)~y+GFZ(w@}Qb zh~8Al-rxg#Q3V@)8_rl{Rult1S)PPo9|LRgb^Fc8dw#@g3F5{QuIdz^>~vk zaN5DR_ELIMrfs9OS{H#S#=m&n9NrWG=(dY$WX8u9$iHi39#9viqe7XH!E+9+MaF$@ zM=lF|l-g$UE(=0_wS`HMV0BB+wRIl*es6nl0>vjrSBC?oR;v9X>+^#X@&`J2khxZ* zKG)e9%^n#Q7;?%0*1d{@Zo*Hj8avnEU~te?qeej>#*GsIk-M3tjC^1p(Kja{Q3_c! zZ{i0k+z=c9NPE9Zfc_~QZD*XaE_aPYsjyOSj@>aES8A-BYf@wC=&ob8xUP|a0#*Jl zjQ?vX3{oURn}9sCkf3jYDyZg#fv}oV=PSR$kvG(v@k9G%+UVGT!HFsmL^K}y*$`Kv zteKWbslaQShCzR;kdpl0*)L4+{)dGs%Ji zk&P*zLBfZ)Wzh1QHBZ}~VrpY9C&k*S#<$OxA!DSU$cC zZE-}7nu_C7)p%kkSRv1-*|^S_m+&Yb6W9uVdQ?#|zxrHu>~p#fTR8J{n|Mfnoj?dC z48!p1m<;`p==>swD`0F+JUGI~8Lkrhp(H{Mf z+Uj5^91RZ&Mu{l6I~zhLlN1mZM18LpByckngPDhin%2_+Ny0sE8bOYPq6Rltjqx1p zbbG>XyU+qV_Rp1#^7neQn7{1P2_Ucv$VY9#iOy*cDpLJY@r`VbiSF`%CfI5PM%aAF zQLQ8FJT?X?cp7-VZC6wFT`NR;LWHD9!KG3LuZZ%dgE;7|u*)hrN*)~ac}YF3&Ui8( z$!#<6i+DkLf31%W*79MU6t!|bb%!1AF#+K_!vwnf$tygtjt6;QS2{9@@L=cs_pz^1 ze9V=nG7MiosQEEM)>5siY}Wg>-AI}aN-e+b_?=%#aGWVQ~IP*KTQNjYR*j#%^>;#S#* z!F=y=kjf;`tK_BKKy`Xc=hj@N_;>`>&)QZdUJY>|(0{CdgjG8hx%xT-QNbAD1#!Fy zhKFMkk?&*I^p&{fKy&>_k3!j8NPg;l%3;3P?7@Ze8z71M!atFnb7lB(-x<)@TqH6F z|GgA|B188<7!3E57my9XhFWB35#3vsEz(|)d>S8;I0rnYTs-}mifxdGO(#gIJo)A%eL8SnM5=`N@r`rBEa$8uPhT^tTcw1r)O!BT~w+cj$CsRJ0~H%CFWqkU@% zl{a@3SlwEa)r{rwK55$ix*xN9zQX;4w{Ays7y3r+*1&%4BitOKjuRK%S=!B=IMK)( zC~WGQ^AGYUK*btXVjgd2cu;Nz5AR26wKG|;3Y2#Jjz)%V7(s}qC|bh+2C&L!HxEOc zlaUD{xELU${s`x#h3PK`rtxDMC?y%z=p#U613$C&$ z)!1Pphdh;%m)pqRAhs7s>voxJFD;N(`!qMS?)YB%q+b3GR&Y`vCJdK?Rbb$AxPY&p!cpLCDdYsPRto zJq~UXP^j$;vSTX_fx;bS?8)H?IC}^L00mu20|doK56qtJ+?hnbSV(=`!-U5M#85s(77req6v^Byi?iTL3{Q$F<@s!M;$UMWPq&V5Svpbo2HpZ#73NyLpp;CLyzS zuJUE4d6_2^O=^xXrxulAOCXMAo}7TXPlTzy$Mqp0a=Mu%JJ`?6;5S>}to6K}S$Z}# zmxw&Gzqlwr+8O-iyj;yc%d7*P6z1g#l&n`_&bUR!9C8J>erB-zJ)4`TiSV~Z+wprt z5tU^Eevf9Ref@qt_!xmf=#Ndlpcwt?NBi}3+U7@YtMGddS}9#uI8A(qI9(D5A* zG5Q76ny&Mb29?n!bp*@CT@x2&cQ76+m^pLKNAyPe4q-s@*}H0ndI>UBMAjwsOEz`B zdp!9KY|Nu~d5SIbWuhi2j`39Mcs)e)lsCB}TWFhwjF{>#yK@&c6^e>AH@&;phu3$P z8KD78duEW!_9w34_XNQq+go=5Q^>jw`TAy#nQP3@9l)q6iXK2|{7@5{Q3QtAb7BrC zn|hTdzp^(6zi>g-!2Da6*1mfuX6XZAlLmsQ`<%ee>@#)-Uc?)wC(f?$5rULa(E?6& zjc|yd*v+)Y=<<*w3l+Dr=w2gUb$T#S=N%x5&k=qXg=}thTD@*_e=WUA1eO{5G><{h zOuuV!_q%roOotb&BU+_~veJQ&X2Gl_Za5>`3eK1quF$#1K*I2vByLnVc+Fm%>A2B* zyawRR_uscs!>RPr*pu{C&B&j_iP&Rdg2k(Jh_`kNlaj5)iAeycx*+;JHlV zrt7@}$19&zKVJ+QxBH|^-1U~5X?&KTQ}3t?Z0d=)Qqn96hDsZKq>vqt!lXVw^KrzG z5y*@kj8Uaer}eX17T>gfGr40(i4AJ1YtYOuVh-aezN?X^dCca)Dems4R&woQIqOvKl47oi(UTQi!b?)o-?! zHr+fLNHiPY89SXy(1u~2xL+2Gn$)B9Odc!7SM0v3I^3OH(l|&V2a~it?p*_vE#N0cEeOqPHIMASlWnRvA9ayc;4at}cdc6EQInmEW|Pp3f?u zmj~DAw`1q7bYatFQ9VK*b~@5I(i7F1Jyss1hv;SQ%(!^ciUycud_gQPeo%iBtncRz zsL87rxb0jhKjkwh@7#Dabq)RNVWX5yUkhJ_Q!@b@;274=r_m%`opZ@0HP6Y}{O&^= zgQvxlgxzur60fYk&~hM;D8k))pMpK~(nFaP3XXP}?4b z-&>4+4LMyq+t~}?25`t~E(z2s!*J+~!{eUD(9k4eP-y7iQ2h_!{s&ZdNI_)32;lc@ z%IQD=?yNhY8)n?Rp_nVB)Sx>b4``tUt(|+eXX02)(2&|7vlSO zxeYNLNWk8oKKQ{k^eybByiYY7C=_!ax}U!|y^)BPfP&v*k%R#QcB7Qx#Ar-W*Ik(5 zfpMyasWL9}<{=0~%RUmO*4SW>l*~_zexqH*z3X}fSKLx*Q7TIZQ{8NaJc>62g;~zU6sfAABb+B zCYCNoz2)13*3n#1rahl0M_UcDD1T=?dY7ga#}>WT(P~GjCOw!IbUpDvm9Q6jJ;j^2 zmWr?@E_(pQH6W!qPpetZA1YyOQ;&5zCivBD;+-_GV71Zlxb@3+ zuWSM};^L1qk9!bCVFFRt0w7&8QM8xVbxbPh~TXzCCT;?z!*H4muPzm5hg8F~fL z-NKhiNoaaqA_@Aj7|UV5J{Fi=W(pWT{*)+F7T6mL<=B%m*=_Wz3no;i;scWeNWo9j z4K>I%W$_i}^8wVaPUDay@N)^fy+l``7+c4^L=3Bz!x(kFTfo<_d~KlCd`+p|I_Jmy z6=kaByTIr%SL2w{P_Vt`wH}U#s;DpGR7vA~^X#aD=3c*4o?f)Unxbdos1dVDvRi*; zM;=wI3k~{(+s^?7Y~!^TY#py)X(UT{IbH~SVJCLp(H%LFdyYncyjonZN+#Gjg66q* zT@+yOyy$TmhrONP)pnToxn1F@6~@f!4n0L(1a1w9`dnat_Pli^n#rv;m>g-(3+oTz zJW+sqNBx+#_cJE`#^y*uj`?xk8S45v-H8!)5{DQjj=e*3KR!kn6=DAU#2+L#%;f04 z*TY^yP?4qj&kkkCdHJIIy(jVsGt^0g@&ME=?QJs$gS6yk38JZ{D4>YMEw14nYa&ISq8ahxeGJ63Gs$tTkLQ&s49OS6fCC-coBf z1@5emB6NRVb7DuEJ~!aMHri1!YP`smc+T->rxn_m$=Wn76^VX2#xox4Q=yT&DHg(4 zHziq9_R1*j)yFz`P%}YM69wa$XPHu*-?FiTUQ09=wO&KzPYJ9*L#IM-iam+Fzfn<2 z-crH7L{rV@e+Zj|$~nA}f3`IrL6t6MOJ=l>Z*&Mcb`B=wqy`{q-mWL-_W6umA&SIU zeI1+E{wtK|l^ZESKxgvytgOtuE@fAv!Pd(!>O|A#mu=iTfjsLwUS~v>DiGP6?x-81 z<9l83OK+pyFpTw0?7~yL`BNRTHh+DDry?M|w^jR4jf?fh*-C=_6VE;mlI1+-0k`DLLo1E#K7{OhKd!_hAc_cd}Be8V-Mbt3`MU* zUOrPO;C&Zzw}5O}GyGF=n1CaZ-X^U>sqNus&K=C*?Vf|BXiix}2s5Mf$!YMa3?LY= znlKQ2$%3*e8sC{tshCT+XkJeFqOOVwB!)BkJ40G^P4M0+bZJ3 zmV?UV!5jx@WC-t9IDIcGu|iv`emVPzucm4!3_P_BtErSziikD04U^Z|A~r@d&PJ#h z-|IK+%11^1nJu9>z zLEo;X15+m&kVg$nD*(*{s})#k%_T+@(-|X%(^}f3n}cH|F$w45Nxxp0s;aG;id(Sn z3_tXZiZ^`ba9%)-*vl((Fc#)XHDBo{;1hZ0ao;-)m^ZN^`fCQ1oxSb_B>(WWS$Jq) zs?z(+I*%#x;5_*+qejG`L-9q2)LE3}kX2XaZ(g)uYU=aa}M|VgAus`-y7of3HGzsYvsCr)kfAal&*SG zyTk{EW6W~mbPZ@1Ry#h3mO96tot!)=&*Y+5|M8vf@Qjs$vS&LCBP*rSbz&?)VdkEl zzR=>lurM2&{@lNOx6Pg*A>F6^ZHXUidzQH>#ZuR->Sa4Tcj%y{Wsl|hH1C0|^MWUx z;#{-Ja#KDJS>MmB$L1Uxd{qU8R}zz*qq)>bhXvG@XsJX{WQ6JDst^La?W<7I*l5x! za8gRxPoj>?r&HO?`o42M!-?d(oa(;XSp;L+MK6DDtzfrTnPpOsQjFAP@sbfQr<-mM z-k{+FwCJAuUdfAIHt@b?2eOb1gBs%vIsd>X@FKLIf5RpRihx~-+k$>CccwTYXOal_ z-$-Bq2}Q96MTvdOx^G#-2raMI>8Lvw9Zb6mS=H6ONXphy?Ai9r^q~k7!aFJ1TYN%r z_wit73CLf+7hPBAP4r$H?*<@|MUd5<7cb-}a|;h3v0_#%JGNxBjjcrzl9GJ&-C|wk z!ms&o!=(rXr2&!J34DMVIh+krMjfWc+utp}(jnxMBu?UrKl?MOWD{5Bby)h8#ewDzur z(Qb0=zGpM5deejMOEd;pVj4|H*9=L>F0-wtt0uvJJ~;h5u#a92M)j$56$ zxWj;GE)Q2HI<$um5B5%Y9&U&M2k(%b+w1#igyQAVO83VDp5-6q=Y>1VjpGnr7w2R@ zAe9D{7z?Tt2gbwCv;6u#iXJ;4-8)TmEDqhfeA^dn1N|i*mIq0?Y{@#I$!_G2ngLmMndmk)ukFa##-3+G)*< k0{Lg7jWQ=2aP>gfBYk_dYoS>V_ev26uBM|}4YhpnU$0cO%>V!Z literal 10077 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 diff --git a/doc/settings.png b/doc/settings.png index 5f3ac27a87e0d580d2a7d5221ef053b8d2b8012b..e63483e622263ae0ea24c42ae766be276dcf9bd6 100644 GIT binary patch literal 60015 zcmd431yoes`#*|G2?7ENC?%nEw>Y36At@~}gEZ0|GXfSN9irq=(lAKp&>`In-93cF zFf{)Ge82biegF5ab=O_%u6qwlHv8$Uhh`0vx$PjY>x_j(@%ZxJjaTZ-f3UFD@4b5Y zLepb>W3qv$@%bH@hHsI1X>Wwj&~34bHz`MxW692U+INdCMPj)Z$6O{@E`ZD5&k?u& zkIdl;jFL1~S8M9*@*Y-gYIW|qXlTG;9x{ZFfAG`gy+8V~VyzzKNUiAh%j{d+DY)%~ zE5bP&?=q{=-W7xqVSd@rhp~;5+FCstDV6 zL3{aE-&G+NkmNGs0{ADr%>9>+|MlEu<^xN$+u#5;M(`n?SQ_`)2mJLunW|xi^}Bgy zOPt+#cNAqJq<*RLHff+$$ojK?VKZR2w~nLH%MzRePE2LXJb(I@JiS{x39(kem#uw( zU3KL;lYFH;Ru$c4$#v!f44qUBvQI{j_Ah+Slr>D!2N9a5rA&>cOYZb-dtBJn_^>!G z>CBJ!F4l*cdWgHip$~J?{c~gWm%1$3AerIRolC8QM|kaxrZu8xa6xJ|$w|&$Tg_!e zu4?TNC~ufjn>vW2#TVUa()-EFB8=wgd$UUo(oZI*~ca4NL~4fd;ypXkL-jK&!j zNZ4k=#~ZB{h80ZKr013<+V^e^T=lVP`Xe^@=I2a*MgiYlB@W5yj^L8WTitK69uNwP z0}G-Ko_2!J8#IENW}5OnVSR5F59sU>i*e}1EUH?3RYWE}YuzOq@OV|Y6B z=9tR!bh+zmEwpZ9s#kUJElt~wWES51Gl!iIlC2f8)UT-B^BSiJ zYB3H>ee=DAw&X>fo4dOr7q76eZ28+@nY8|?(4O92#Y+cXULRlxPL?oer2jxGK7xMt znJZjk9W1^sli?RDUX-aVcTiJ;*t1CgF#s}K>X1E%oBIh!9Dpt>LXIG@pP~*XaVm|i z>=*a!6(Bs4-weB{3uYvVXHui<)H}Pnx*YsrNJZjthrx-(#h*i_sgtD2gK@*Cr{dyk zR)MMV*BV^vKzY}S5Ky;Ybh>Uowh4CRuA25uT>2#sK@C@c#ydsd@j!+ZA$-8w1QwFWz1}@Cc8_3RQ!-q z>f{5z7fA~X3(9NYv$Hdo?>RYS|FoWr#Gw&)I&IS!e&3A*^0dkz#Os)~uABac;x~ZYkJ7C7@gkhXbwphKJ22 z%FJ}0D!y;j?zrP>;V^!UH&nJoT zVfR>U#;Xa;6_|?MXecMwHb0+gJ5kEh2Yp_drs^*JeaWkuuA3}l@E!V7FdqK=iNt#bX5huENcv!3I8)nkDzVLF8sDv%9OkpI@{)4q0f^ctXub~7{@yPx0 zuSaOVEr_%ox4X`XP?@O;PFK$!8`B-v;G|%oiI9Kkl$Fy#mG`5KvK9*UFOqtMfPer&rA-mm99@Zw~i;eZcXJe|)n%wB}{a&@o{v?Ixcz2zQ zRW$Ymsm5b6E*$G-nLyXqjt<3|w<(x#UxXXuvNSx#ztLdoaC=$PxPks{-rE=1NsG|I z6)3Xbtzx%HS`*WF;hR_O>i_>!JVKej0DfbrmXticq2m;9O; z9F(x2_5W1B=b#1x73KW&5{}!))#=$S`&H8iD=4K#a%Ae7OSFO+;G^H}B#d%^8#w&D zw9*4HXp;z{Ai&|tG2o7Gul@qdEYD-vyBY}fq10mn;KfCowFc|^J zH#Fai2wD*!8WdYGXLbQ-ERAm-I4PCtDX)W%pSuE-2U@D3>WLsbGMYq~1#rj9$CK%5 z&A=Mf#dAI~UW%UATh878(>-S*<4sxx6)0Fy+C1|a0U_eX2cZTWoB39%b}kOvirX`@ z)5%+2HBoiq!bJ$hk7a#!OV13H=WGj+!&X9PGnRriraLee1^0_q zK~*1bTL6hueWG|!#9I;SbBpw&+H;rC+wHN3+o;L1;MsoLx z%TpYDRz%diaMvagKO*X2roK+PWF--4gn~a9K{Oev2C^6$lBmWfB-{(QzARPlYa{Qb z13uwV94;hG9z`MF8B{_gKGZ^Oq_~OM#+&B89tgi`fs>*W4A`ay1JT# zAF5;Bdw&x+;j!0oT=)?+!m+K7Co;*Ko&N-7X}Ct8NucDH@G*bwq{Xv>fnQWK;FORQ zigA9mvXWAOMKfE~izV>y8=0AzE#HVZ#^y9k9Xw8w4OF4cA61D1B4%T$Sm;J)X2O2< zvN(L%*>RiirLCl3#gX_JCmWVUN!7|<4*Y^Y@*rF3bc>9`NmMy`w@bigl+spa!O;SQhV9b zXJK7uZN-fxaXNl-!g~*YW}<5}A_dPXVnl^`F?e+k4C3-`{K8_caILMRGZ7LJ=AI#0_EB%X?qQ71j>dT35#JfAH3fb`Ez`<7S!d(Tamz~=}Ap|Q%= z*ZXI~YKDkrc{BLbD`VYW4}3Ggl1x+4jc+J#4-`0GowANq?a|*c8F*LMKBrN8q=`mf zrk-r?jO5NO3iCEg!D;8>F&lG{n!7r<*4EbK%O3+eP;S@=M3ic$ZP&&=H*)iSCwL8H z2G{Y^+mkJ`pf&xE(xi*1K!8#FM-EsvWyP@TOh?k^8-&G}jgK#WuU&as;>zHxvnb7g z$}MM>$B!Qi3v1(llpiNS^m8JS-HZtF$T3~&h2kQp8qar>)6XK1eyy2B8MKn_AB%@a z0y3MTkX%6h6nrwf2uZwK6m;M9@@3h2*IXDS$V~$S1AnEnEH4Ho`59VStbLH{7C8^QYfXp_LurboSdBW^p>^LFnD#CRgU+pXwMN&9Ph0E z;w^0U5mg@tEvl5dZ{FXHtw`Mj9o#ltR?mb?PC`x(aN`xtQ^oqy%BlA+BV-mJaT0QlVUQ_sEbW+|*_V2waP)XHN zu9?*Jkr}h{Qb~WRwf&ii)K!hQ6;xVeIzC~*Qv&3SjA&z4OQK-rXJp&!>+4{5z^+ER zdPI`1uWw>vVwDJ$UB>i$OVB$-2-6H|8d}HTex#ZSs*Na})yoKC?9Ns@q_2FN@l6uW zK2porx@BB4<;!~FJo(Oe>yFy~o&{PQD!%IU;*YV+v3jhB?ZoRU(0HU`RXg}(ADZ{$ zY#iPWZn3uWgM;y}PtO=d`%tnLh(Sxi?(XiPWce};)_d@3x=Mi`Eye(k^F;M*=98V> zUH-|<@{)9T#5Oo0NM(`JMxx`CZ)!)O!?gESBBa21dbquzQCIQt?eEYV=*?DhUVZlF8! z=F!PXb#=7{uD~vkr)f>0qs#B71(G~tD5EQ<;C`7Q#}Ud3v{I;rn&DMw>TWhNe{3_@ zn6|Aga=&z#rjl;U#XPg6e`=&n$|lrj$)V^savWn4Ke@*U+j=tx*C-VCuWxf41AK;8 zPzOep)^MQ)wS5=3dxb@?U%NG)p^xO^hitjw>1m0>Jye!3#eP_Um%hy1=LoQG-f{sP zhxo!M?Kwi?nE{rM7j!$mgqDh!wVL2c+@$I(ZuUQ`$YCfU!_XVz3cX|>PJaqbmdeLkT8}a*93b( z>_ywf?qkSk+(~h9F0~NNapzQ|F2wYRP=O?o zeBED7mdph?(Vk@N|7_;q3+|kaYzMErf7&9Ec$-jBSK#wG3|LT65!1V(QL?wU7bVYZ zBOHH)xM5Up@(^GRE@Oir8=arL(W54^n#3*^ac4O=adgTWTIkoRq!zI|JU&*3Z;Td7 zr>nmcjO*)rjWK1_w0Cac8AbF-7e`umC%L%}vI?XaQY4+i$P>qNMz+a({JybWKzs=R z+;$V)8+sD;ElinAl0Wpq@|mQqT8oUfev(>;6*=T00j^%#oBv_qJ>w@&_I2M+F*q%b zge|wX(%XYN2SM2q8;(b#yWk?1R?@J}2yV~#B)}?Q!Q`_WpxrQHS!U`p4DS|5wEY&? zVt0p!=*1d?;r$|77Wylvyv>WbxG=j>L#Ezs8DHAAzk7nvXq5&3mfhvp_4AgLCyu0H zM~yBziM^6#o+~XW5k#%7uJ+mXPo)OIp%yr6M*+)f9ghTc! z3Y2pDt%k;fYI2hRUX1oDW# zUL9FB+7eMX9IAR=*)8E6?OC1?*I&2jFC`#!ofiZ41?ZE zW=MIsB`#gd;&slms4g)ElRJM|2ejY!sSe1ayB>D+lF+s)BIbmCvG=2~cDW*>-JTI$ zJpyzh-s_x{2(9xfUw+(&_V1~r01TMR)X1PzZ)ytTMlCHZPcS&rFa_B^j$-8Ok>lM8 zh2P<`*VfI|e5GOsRI#p5-tmM1}CwDi=g)4JDh_~?oEXYE zxTRM=zs4}JuuP`n-g3XQNOKEsY7#o8b^{PgH}jEi@{#+-MmKj=^|7BtyTHdcz5{%I zxo`35Cc4w8R&w-rReIb4eG{)S0LA6Q6e)^LPwdyqOyPNhjPe{-GIy~T*NJir*pnYQ z)fwKy!OnlmuASMVn5&(u{M|;k9Ci>^rC9Z3ihjy_#cOu)w>v-#d%9~L9cB32Q*7ng zihGZ9k7Uv*bVZk6?;ZWgYKQa=*vE6*UMK!Mxd!yr#34;iBM{K@EN|`m8xBP+16aEE^+9g0AH-))8+o`B}fRLrmi)4?EJXrelKl4G5m0? z0s(nlw5ZfDIR~RG@ZK5wQ54)-N9TPstTT_2;BmY2v%jo*3AJ8+A1PHyMLg0QguC{yOguY%zM;_zv@nd0gd zt3vCo*fWL|N%)@X4jAnxF;;vylvk0|q%R5_BfzdRWU;zDRHhb{UHfYbX zTS-9oM6!@nj0{Y)j_ML8rmCUpB}h4X?wA5 zOM4pCmEA@c+YPeM+|EuF>-i(5OZV)!ehno-b?niWI%l(;Hc@lzHi|0&MhiR$;dpks zw<5j}%3O_Ley47K@vuBlc?Oo)=gFn=0|B*0{w{aDKME%JfezKLxi8l;xe0Q#QSI9S zytvi!;`8Gd(q#77NxaPtP98=!-g3`d-cIKG)M#ns|xNF5|mq z6p6q5L8n2#2|3@b!vDe}6(u;!vdX+F#{p6m67niQyJ~rOShqI+kv}79pcaY!Ktf;P zX`$;QkYZ*@^R#ipn!on;b&v1vVn6x}@~JH~C>g)#Tr~@Rzxy5poOIVI;xVs8aO~6e zpv5A#1-`8al<*vOeHOpC>*MbJXQ5H%RxAjv53d?{CcX}(KAYdZ&;}K-!bI9PpMTO{ zip%UkRQ#|9y~)&C8B2J|^Gk7kH}E@JrQ(_L;(a`#Z`jrZ_V&O3)P_e4^hKqoWOhiY zM*G)l(Sh|+8I|UPn~wv4+`C{)^de6MuK<_RV7;IgWfcQ<+l5qFvd1j@av;H8n{YqZ zJm0d3fB*7xi9f85JZr>lUfQ>T;nOZPbmpHxO6fbUT*wK_wgx&`RTmQ8RMMqCqGok4~?+*t5!c)WInNV>-d!BDpRa1*??68 z5uHgQ;o>VEq39i&BoqGiGM=Mx`GfrLhM?b2#E<3S%9diUVhii>nlis1vM0qb7aP%} zsw^W6;=%5{seKS=A#@oA#fPc#J00qFb#`_?&&Gc%2 z%)A715;MggrZwH-cn{Vg897@&P*`1JU$JLa7}LhoW+xr;A#YfJhv@;B=q1 zgRTB{L<&Cz5l_s6YttR;!#tSDGF8pdoxP_Hm=H``=`ZR}d-qwc35;$ksG^S?wkID> z0INn@tM}$}_GdcneQNSmPb-(o?T=^eqb5z$fvwdDoCqbN-CUc+96jTH2UW42Z~Z!R zf}bhC&)h@$brK!(*^a>)U9Ly*bU;#zu<~lMp$Dt|eNd|2n{zk)tTW)y7RL(9G>3K^ z!j9g3cC0P+?o`BQ>T&|udQiyhU7dSHBXBzI!w=e>frutSY$Ym)A>7;}=I9js%N-0N zu^)Nw4VVl{W_$8a?^aP6O&tMR1zg;n2{X}KgR$$w2UQlGPkNUf(^eec@C+)tM|yHxH?5DqGJ=WUux6H2R}XwC_yt+3*d`m`o+hb4!P14M%J z{plSZs}^K7p%Lmaf%tndoG7~ipVVHVZB8Vp*A!qt?r*s(v_?%Zf+W3Ou`WG&BXzTX zhQtu{{<+?O`@xeQ9_5&F_Yix>PutUo_f{hiMD02-YQyzyqK<2%j#i|OS*p9)ZbHsC zPoZ~&@w=zhiR$*rFR4Am)pNLf9rdjC+0B0E^Mmz1PCw4>iK?o3qP+5syZ7DB^mcId z;8MlJ=3%m25QM$RICo)#xSFuyh?Ot|YXL>;w z3skJnjp11`>UzPTqgn0;F}oExbU~7=e5;H4;|i}(fq!`(^;72p|7Ak`=LP-%{3+~^ zdbY@XT?^$Yq>+VDt$XxFs;26gc#^AGf2nG%zfk*=;90w4Yfonk|!76c7NJ%smE zpsN?Q6x5(a9#ll3ijpCo?J5CS3l2v#SNpN6Uj-sK(AyVtgdg9USFEg$0YVg0$L6kL-m3a%m-9#_1J`YU3=hwFlxt1!y! z6*h5QfCah&2d=BnqOKqK@0xp8qOU7q{ZG;V)#m?R^RMXZf^Z1@@@Q|d0#awfPUQ{e zmVd1uqyXUd+wjp?yN3;4)?7;9IxQyI!l_`s29>tjXt^(seTUag{ugKBf6^rWawh(( z{2xxm+Lh}6#jRKcUsbO6wWU{JgQFKjy(@mA1xeMcr_f#}B8k_y4c)5HVgl2Ae0)Ga z!k%?Z&$T>~5*V_~wsd4fkL@vup~2|P8)=0#z>JIRm2o|iJ)sk?afdf3oO<$0r%#GF z2OnJZWcK5MS6IME+#9!Q3}uBaeJvPatJbq?~!2 z2A?>7#dRnYs{K-XjZHnb%sYtBz;~NZV%KZ<+15Mm(NhDAcRx^S<__;ZL(fcDC_y}- zZ8B~lS8S3$pBV3m<)MGBqpGg+sxs6AD5xg<8!+u^AgqE{!IRH32iXGW9Yhm)S2P2I zDnk5sI?gXmT?K_@A_3pM>*{8<2YofzfB`s#U7$<9U6(YMb5RD#u+xUMwVMuDl@<{? zQj;ubvA4`q720DS`hcmu26i8`D|fF3x<1`r*R6T;I&n5Af~o}apJKFFwvG(E+Z=-x z7V!j_6;>!fR5t(9#8iV*r4n1dcCP*Xsy8%Dd{heucQIA_P#B`lwrWV*!un^hq^KL^ zWl389ZxsyeQNQ8>xGWXXGP2{Ps6A6i~+=$7kfTUESs=(gYdk! zEJPsFzhw+&8(#9Du6q?e=MbZ=SAZB3FCODe|I<2Mnbu79@`lTct;Qjo7y{YUui|&R z1o`+W?{&R=p~g!pI4F;;h8-=tLw@+`rQUly?t3z&gI)0v{=H%>R8_fl?q0aQVv{+? zr-z5nktL>kJEBH4ITt-&7>AH+sK4Qf0v0gf-NOF%xywwCm`BZjHwCC4u!@O` zBNy@fkKg+E&=*j~AH(bFoKp~k$gkJ!fmHXw18QTl+f_T@_YG}rZQmwmX3BBU9SaL4 zS$q_8yEz!(*Zhvx^9x@PHaC8#R{huaFT49AY<|~%w70j{^c^(N)os6Z_YGe-3WX9? z`w2tt?K#J7O(VIux#wARDV1_eruQs=U?Be1*4BZ_#|(l&5sV_S$%}{jKNPVSziH?g-aEs7zbl+{_)~F zSSDrs-Tmu3hBA_EWp5uwmS<>mJizL~ev>sttczCp7A;2&C}4c`y*zw$=Fi+5LpR9; z=Rf+II@C;$k3X#QJ))(}7Bk@C;##`FKrp+yYH4iziN2rI)7e>opFb7jl(S^lil5N* zZWOGbz{DADZ|EPi-r+n_v1F^Eq0z13zGDC^XySs?J+SqiPL<&_N*IcOz$ZIM$=O^B z35}`|3v5svJvODu$w>qD6yZuKzgRk+XOENzSt%!%_wE{YH#SOr3)hRW^VB~bd=+(@ z8L9T0b(iNy!5v!>0f8uV@oSHomj!e4rdu2?A2d0fNQy9xBS!|T9xB0zBx7z;y@jzc@U)#R4Sdh5 zPfdFLOg&||TfKu9CfSAgx&oNPfR;zTa_OJpnibCqFN+Vd2V9=n93rD zg{O~qP<$su<;Q>l!}<9y_cqQDK}W1s$=6nd;&orVBt8ecwKubV_s-V2H1Si2rNzSi zPms4vpCX^4!zIxyU7XT(LT83JTK<5oR*|W9=^g5}8{id2fdL9PcvMu>n&h*d1;@SI z*9A@8I5nQcD-);Zl7u!uL?30^Un*2$PmV zpz3A+OO}8Ijy0nqOsd7mF;wjf3P$7CTlWe@L5+mY(W_Y4d0Up*lTi{to426Ma5%!&94J`IViG)*6KcjH6`Fr;J7N=zxGQj0@Onf4A z;mGx_OqlWS=%6)M_{p5?xwQ+xT#mv}pbTA#yfk;Da4oX%yYO~_9LFW|-~{RA?Y#^t zcLR)~MifV~f;9-|7nS5cZ*L(yH1w!@i_S|%+deZ*#%`^z2jUe*$=b0t_}Dx{9Ui`M zfzK24_Vy;o<}#+6wd>U)%?M4D=1-qB`ZYQRDN7unP(mV8c{9GD?RAe(8!eN#l!Yq7 zI?tVdX2n*{$0+U;9>uAus=l}0rJ+xoLKV%o;0v^bQIb901YZrykZtg+x96Z{Wktm= zWCN@`v}ny#%xXbUrAJTj&Jydf&dIcoSghSMx~RnfWxg}i<|fy`n~`#l9Awor?1xUW zXUl#3KbWP2DK8Z{{5h6qY+@pKm)Vd+CxVP)b@>Y zlBF3geNu#Mhh<*0-m8&^01@}!h1EIr#ezA0h!Yl%j{GCq6fyXx^eK5tw?iQ@WAR40QmeH9*GoOa6CnyX*-oT?eC` zfVyQP)7$9%uh;udrOu*#4Qu5oYTbO)(-CCm-Nt>K>l%ep`03wzlou1~!v1c>qq_HK zOuEF%N4^yEm5wN|v87AGXV$N_?z{xohkXom92>3J zf#$0kv4SsNlygm$NK>RsF#uQZvIcCX%KRcCP1lqs+)|i&vzf}lb|0H7U7b@i`|X?* zFR}dd*T$#MZ>jYK@Oy{yJN@pA7jgbQ$FF0rB>%+knIvv3WW_rHVvcrbrR2qIgK`c; zo*GId@%-q-M9`H)FO7;}^Hg+LNU%g|N1=0-Ga{tE;2a(-fphk8Jn4WYdh%f~zfR&icYZF+ z_Vk42EIU>HY9oF<{Qdj)RnFS9F`@FZGEJdV^$Hlw&e5@7Y{g<=%Nmm7ZEe7MuVbl5 zdo+G}dU|JPhxh0H&DNBZlwjvUAP?}JVtS-4=qJ+E&F#sAouQ%Oum(L1%}*91TU+Iq zryj=W>@Otb%J)1MurLBsU@tMjvqovQISQw_Sp#G5js%Q zBv0Yq+}sor5wQq%HZq#q-yhN&7#)o{)6$y2oWbUPRaS~=0VE`#adAZ^!XhGPeA>zH zX~{_^9)y;AWZwUN8kycJk{1kHDWHi=0FL3>i}embrx2;ofKg~Tp)Hnnt>$p*Puja} zg+L<@k5OWWo^*R1PKr&oqF!ZpIB(R+$%$Kxa^u`wO0V0PfTVzc$!9dVwe#v8Y>F`r znO{&aacf+D;~vB++Y+-GzZxPHzrr5YR`5Br&5v9Al%9E_qk8TCg!G}fr|t;~3xg!v z1_mN?dU|@ExQ}P#jLRWhn;F%-yv|~Q1?+0wy}h~X&CM_0giodk;z~JB@x!d^XfB@f?U>Kz=mASfB(&WR- z9(*A5xcJPGMYzYzfI3DP91R5>KDU)2x{PlFZL4!eWOs@UbKnY_dZ#QfM&%pJkbJ&=; zPdIxTFR@~&@Gvtr7wPT`%^@=EGg^)SbyiAphH(5_IR1XPDUbWQExWSWqR&&LOd&y0 zpax+g1D8K!vS$@|-c10*%QkcEJiNHzC`i1*FO^7)<*my!prV+lh5Ad2fv)yM*FY?W1JR z;yg-^5u1BNn>|_zUOB*x-?AXs_i2o7_H%v178{Go<8{tod4G&^&B8L6N_4*{%<2xW z&E_L+TfCLj{&g7DTl6sKE71?Fx53*(#}XrPLHrAiiRV;gcgUU7@GG?6O6_J|_Cx>@mgE7Y7fHa2QDV@8c-b3~TTD))nw`>hy$Z0mYviX+&L z=Ej*7SWd%pAC1mDF~5ff6W_cB=CH7ol!Z{ZlBbqGGa-L=kdp=o){+ z!g5_aZL#)RNTN2!gm>x^D*GX`vngfxz#+pmzsaxTKg&8!8j(Vh*miZl;Pjn{C;Op}-sY11f|u35zpYg!DVIJINjpJPsIxS!F3Ltmx0G?sl8m3MC;Ph?yB z_gFhu)rw6p@wDGhN&4T)y&+6*Z)fGf!?#xCd%C)Q!l5JdR8+rT-g|Y8V`5}a+@?d$dZ=Fjs_XYdp&inz^+#YZWl8=t*W$o?l zd9oW|o00XPUVM97+h}%H^rj9D%7kkR^<{l$E%4>&>1j=1hp+2KWpKu7RW4CgVlMYn z=SvfL+1cCs=K%mZCqs1mh3mNa^mZ|ciNp1o22;&V?1Rk9PZ*Qstw=&Yw5}o8JG13j zIbLgMYikRp8*sb3y3YN_fCjaL6TeIWiG{WtU1RY_)PsJ-Q*$}T#$H}<%@?2FG8ho# ziOr%8Wbh|-g>J_B%gniSsV@+sY zNf2KVpg@CVtBKr{L{@uCOG`y>sdaEpCKteJn|dhjNyy|jy2ZZgQGQt;F;q_~kKw}p z(W+>~Fz6qlt}$dRtk-gOB%wkZ*f8t$`-@w@<2`6?MJI4z@de+Q^oircF<-gRpIZ}q zz`aOsmb+&~>e`jfl4onnRAo8ZB$mkILYn61jk9javtGZdvvof$HoQz~rhQL*Myr$h zL_2sx7lOVk?98T6vd#CaPv;0RC;QXA1hizlxd&z?d*zGYe^=<^2mj}c_~5Ma4-8VF z>GFJVkHDQphVbWaN!K4!Ds$f!`{MhepI&^4VS{|St7pr`uxy5efw_2F+!LKRR)6Yo zp@bO!$$tLK%3-x06Q_Xg3#IpxRFD#7w-+gWw%Hg4F(DLd2v1kb5))}9@kUm_-m}r{ z66}5itUZHrz|XU*mzX5h^SXzI=WvnA>6TokQsL}C$*_l$N0F8s92^mBRl=sd9UPGU zpHI_29{qgUZJEb;h!I*#7;JZweze6IV5r1$JFC96Rkk*o3wSkiuIfag=?b-%Z?y&aoW=tS)U3`Gn%Ht6Z-^ga@VBOg!VOxGzdIRT~ua;W>X z)@?Vn)d~>9E91MLh_57;d^O;iGT@oHRXqFj{5x_S}YL@RrW*)1HHuIVBJISg{KPqK~(-vLcO+K&H>&>iyzx?e2ta>s>#81U?H2W-64XGQEm)~=*seRDE7KABzwmv5b_ zTMw3w(Vuz_&Gvo7e1^u^sgl^5DTLNP=zfG?LO4%)^k8J?tS0ANZPKwms87H5Q8LoC zuLV^LYjfDVU0n3bZb-QoFQsoPIk~iC<^*tp53W6N*f9`9?4d6qXpCcYlcvtnFpiN6 zssmOCzTuc{OlP=%U~xF#FjGK$$FGRfq(2K#_p{d0IheDvgv-zS38roF#G7yHOw)vc zBQHWupGjbA;x+N*?6OMYUdd!d9xsXjsEge9CX#vq?zI%I2b0B!y5pVZbt|j#43({| ztebYW;{XnoyERJLyYt*ETlMyo=zs`3q-y!?DF zE-pZ;WcTB{0>Z*&m6h4sbFb3%r9@=u-{?xWsyOeS)6nJZC7+BJDts0iB)M zJ48b}q{!}IEF+|@Of>bn0C7KuwGjhlo)oXm&OVZiRPBF^=@3W9@+gIcvum#6>VEQs zlY^1b3p_5eA~|F67#o-X}h0*+C_dY*e0$OId5D-2tD2I8nb}5beM7@u>8BIMVk#}Wtgr7&o9!pq_f}DW zMnxqg}4g) zE7Zy8E%7=R8&jVa!=OV*d24kbYz26Tj9cdpPaHd2`wsiFVuOJ;v>0_}W=3Tbzh2 z^AINE!;>=@pxxlLG5Z?}YEal2ZEy@(R)(?!TztdtIM=Vemc#Z*hQ_fd=ZDlCZ#V;9 zY1XV%1b=>$I$yD>X44|UP@&rN%cjQkbPY{4bGKl`-i$Rp|*+bBirb61S zo6FmG5CTDYCX*z@uT7(Y1vDyAy6m1OpsPr9#k2Jk#0k%Iw6=J3Kjdf=(WJ9&KV2DL z8Gtz1_WQ8mR(?UQWT=x&6yfe^W~#e-K$smNuMkGIqCg^bgqlU@emZ$K(^FcP@@5V7ur2pn;BS z$NR4}2mu@~%B*!kL`YsqJyjQ~-qAdmXW+1bG{MWwu+2Psk#G8nvE-0FS>bvz{h1{U zmt4h5 z;JyuX4b<@C`vi(3@e#8*XxGLg+`aiaFE|?cu3MyN0NcnW;^-wn-aa+pqTxnY-zlD- zF8dM7KauhR$^v(=rPBAH^%sCyD+==)=HWI-)a>hf!bsHXi?49Hwvi@v>)XSt;sQF6 zii_&l#+E(EcQ!A2k&XxDyI^VGX=EXnmRVVxTKD1?P(j3f`bINdGZn;g3u6$KwDLUa zFJoZ`yaT2rZfS5}0Je=Pq&nA42Wze23RT4CT3is&2rrlb%v@9TAAi&RVqwE@5cwiptYBs7+a6dcIN{tIzKMQlKCYxL6vKG#4ImL1`p}H@y=IbP;&Z5; z+aQ+Cus-tpQ6N<<+j&$LbL(4H+-;O4wuhT@frVgT?yrx`GHKg)Ep}@@r)hTmL@bQi z!l7gneaP$R@S#s~@HBT9pE=PubVI7j1blU!a>E=NiO%;Vg@q?`*rb zyUsI)+UH(Td;t_oC@IMtBC#6vJfP7~F=7Y3 zOnNO1Y8`YuU3`h?T>GIPhbk_aV>VCE5re4FNC=$Pu|fP}XIB|G6+_7Q2VDF&CiqYO zA1v_S>HoLb;HB38kFd*sy8G|+pYA3jr;4vY*LuvS)c*!q&#sp3`t$#-;Qtl$^?z?4 zhF2d%EB@prmEXrl_??T`-S{sXI1TjEG_wcVy1BnxZ2fio<*J7vC0y-g|D6I=ugN(7 zN-xL8WwrmS^m061mj65bxA=eZ{}PY&H~)Vo9u^d^)uW_Qv0$C=+Vd43fobK^J7>1R zd@GX>9j!Wk4UBRwfLWuJ_n_3kRMB%hWtsS!lo5AFrSMWhN?EZ9gCpdB)dLsVZTl4% zjh+j9;+0qar4nf%Rr|lZnT6k0&|-vaRn` zv3>iR-L!~WMD-L5DP|64rD7tuhH|rq7(&1zian!9>#xWI@K2Dg?p#CPCxU+n zQM=$>uto3h-}umgGccH>d^CQ8f`UR}9EsZBUww5wlVWD=OWBOC_4UOC1e_fl!o|Lj zbH58tVEp|=9eD!2@T-CTeKO!8AQ!=rW8Ax4=3R4fY~6IyJjk=gNb7Pw+g*I51R1`gWR~mAm4S%C7UEf{ctO1@cxlHj6?^C%E(lTTTxGH&$1Z6S1gC z3lhQQJ08y<1{h#QaF@}+QUyafU}Wi=oZJvfeu059nQn9?Mz~9T;uqCs_QUJ{1ynni{Z}t@ib=p*P)~2d{1n{u7<{*DG%3#70 z>5*&d={c4U!+RZ#(V`F9M278884jbVsk_3;)Z zN&j;J>_7HRO*W|TSTncP*w`cqo`|IhlVHxJlSsRh?^^?6 z^MoHAc?t;qCj1sI3EhGB^Gj4b_A+uTvCRMc-r}?`^t1OU4^@Z{cIWRa}Odu5N0BhBn&ap zqzbP|`&=%!@5mgq^I}OuON$nPq-WF0%ga;xaOT!zb2C0QC7*r4tt_|LcvweXT3K5w z`=+cS_(ZY$##A>-0gmQCIXj;Sof&yZgI1-8e*bA*uhT~mK}B%-AxDo8w@B68Kr9%C zd%TK8Z@{T3NQ9sNr3tV=Qq}+YQRfhaF5)D029Y(c1jVqgXj|*w7+rN;^CJ*kLF(fY zCqto*Z{N~S5>zU%l4^*I%TLnnrqrf);V@h^*SrKkBjT!SE^5Q`!~h&-9c-m6uOS%EZYN8G~*{O+7Oi1RK{WIB{t39w}yTyQ3-{a_$|vvrzj0s z-hclUKJo_+)q_*MUhG^@ zrFq(M$8T-r7g7rXQ(PvA$i~3Qxk#a+E(x3qt(xt@S7wd>Y*kPoi78EN+E#51lN>i) z;@p1snEf%9`XvLHmzV3&fmBdT-Xk*FheRI}ixtSXS!s>atXRqT1`YY15y)(tm8n4W z>#4GPPzM@zp_E6&g^lJzsg{|}kxHDSL5T}U^-5y0NsYY_9728$6(Zb)~mXmMGd_Av;=T{gY2 z&{S86>Lc(XQmhFCopY@?f2}4c?sWf;8N58Hh3-d*EqR-p;#X_Gys%URc6W77&b!`# zd`vnfXm)9Zn%%FR6ypg_d>55Bk!DTkVjtV5Y5F+mF=F72USd^mup4!a&BlzVq|hn3 zzlq9czRcZ!g1=?mf^TeRS4R1&5!PQ&0a(KM@NPZPzO2H*r1E}zzyr?DJkcDgv6|Wi z%^+^5Hk`GeD{Pwwx13z@7);Kk-?M`=X{Vmk=mD+K`KMFSZKlrKJ_H!%n;jiB5O!w3+qR8_PPuNERLHL5FpVWzKv5d)r!2=p{>F3cJn6#%Nq!2`?_tR>@K*? z%xx+cVt}H0)d}(%En0o`Ip%wmx)rG&j=yr)f`KNY9nqXA8M(sQScn2BC0q zak-sN}oQXt@5A1KsM0c|&Sow0__49a_C_&2~rZV2)yAc$BEZx6- zVn+H}YQ@Mvj_!5nm|n~N_6w{D`&nLotKELO z-R852JTu|v;7swQ8UDSGhH7+!HY13qVCX8!I+<+J)md1b4r<+g4bY9EI$xnr^iyH? zx8MHw?DeFe-I@QDV*on=k&M9*^~%80BNBr>_a1AitsUeyq8vT`_9j{hU(pGK5n1;A z&qa0_Ri@X5Z~I2*JAZL@#U4Z{>(HUjOa83bauh+I$`dtz$n(B^NxFZ`w7EKk{Wu^# z?4aswZ?S_~K9P2+q@ew9vO7i49^T-}K5uGOHLQmhs9O!|=b{z$c#hm+!~*nG4@&;Q zm?iYp7+mDvFOiMd&g4sR$D^Y_vtwb3g};X+EQ)y$-Dh;#G7ALRzv6gd8_dI$6?9`4 zfXWcVm~NLV8xUFub>)U&k#hrRGl7}K$owd<=PtTak&rj%a(x$k^1_>3n-#SY>JX5qudUtV(Qzra=Z8Z^sYfJ2SwBGP zaenlu!W9o928!IQ;uorstqBVcjWh%pJ`%ZB8wYS*A0T8VY!{K=aF3bwdboh`68#_J zY|<0h!R2*WR)X$qiJ$q*;R9aLGkIYPEA&UR_(LZ7BIrjDrPs`CRnn8m%+IAi#hc;Z zarZpd-$v@w{b8BV8Qc=_v3v&FO;d@$2TR5`&|uCgJ*QspBtOFwE>dw~eaH+?y90(k ztw5(lBU@B_Naj01Ql!cwZWTW8Q8X#ig7+`PwL}`<_u|5(2_9G~f+?1z09ccLNl|sU zK3to>K`g7evKRe0=w6t&2rpb~plbuEbwV7cQ|xIg=~qCJQ7U8heXh}c%qYZ*$uB~dhN$Lb)n{0My6|3TwYF>XATRpnbNP9{tbS5Xy2-uSR!1A2$= z11?wH*&-YYq`X?5iH;!t0AqXn0L2hk=mkYe4)TEySnMZr$Y$?~BI=1n(CdYiRappZ z1Np8K3YsN);lkDO7wU;leCS~QB}fE5g+ziond=4ZW)~JiNLau8Dew)Ci*OrVFv{8~7LM_)_a*nyiXSH)1=jMRcS0*kt{e8-)Ykok&_SD+xn; zJ3IU-r>k@15*!bj4uWLB^z`*t3>E_Zqj*A^ZR0Uhf8Jpw@LTu7T;Qf3PT43x6zC{q zQAmn~MgC|=vc4$yVdke;J04DaN!E}YNduQ3%nO8d*u*r{eEvfDpMp1O_vz&538@E) zdgE-8bFS+?5%dP|udn{(Ui8FXtm}98+`WkTbh0Q0urqvk^p+KM+s!M%w{F~u3=#wj zT|owREEWa{{M`mZA;jX2@Z;!0h;jPO&nmE(y>({KCBPcq`?YeiJ(ojrr)93R=y>e9 z{ch$OMbX-8Jv`fGU=Jy}2~;~XuGZqt`*+tAW$EqBYGXkB7ERMlgX5}|@ngh;#FKgN zFGBiR&qkhTmBA3El{)Km2WOWS{OZ|yFxP!BfbRvVtkr=aFsrAw909P4t;JS6eyVVQ zA}7Ldka!@`tg^QR=au9rQ)0hkcakG!9ji)<;U@9d8xB`m#p}%mY7(Ui?V}rx*=N8C zYGGD>&TFsa^3{Zmk){`z{-nLL`X$Qt7=zE-rRNxhs%f4-HeKFn)#6y`%pSCv!Js%F-6!a%qhD`-+Pb?X`?W{eAJEG38g)$<9sb ztDxa)IyV<7qa65W9aiUL53wfN-)*3ka)BjFY#!<@=IfQ}?c(Bd5m9tFF~M!nSW(&N zULZsHI#ARbDPt-@PxiZGFvTUPVK+5c86IAa2s)U8Ie%DHPn~*CVgy-L*%Y0yFDFs-?(r`BVy3s<#LCVttcJ zShQc6Im!9uC&k@ZoEjeQfHV_w@zC}#Z1;d*G?Vi^QuH=e60(S_Bnq!P9~qNAe<+7y zwuumig*BlXnd|=86V9La0p+ckr&SMmroXK;d!^#4>FPdMseR@H4q#WqVrF=i0%mWJr0PEecOM?`upsVs5lvu-t=g_n=AYJFJBbShGL2RX_zsQI_UcWjZp zI7(7^QNGT#h1(q7I^){?O_v_bi8ji12$w#Y#98LGjg8WwGzRz6RC!R@RaNh|b#C+0 z;g%Nobuf{_px52eH^N#+0Q8ohu0Bt#klQ=QAbD9S{=Dl;CwSF)dzElQ;j2VXLwm;h~c2Wm<7W>T&DC7shTBZ`jt3bkxrU~FPUPR4` z-_!WCQgS`F6iZ)Pi>ub&uS1$*eRtm1zBM>HPgC)&G_D?IAGzo>yt#rJULP0FpXS*z zz`P6$qTnrlH;Wy>bV8R-htAu*mKM)6lh03Zeb#BsL`RZK)U@lU(I9Qi8S2!hy$04v z4paVSX`q}6#!zV7X$j^S^)Ib4?{ty@lUDooJUz~PB@7j(Bs1Mfu!0l zcPh3r_ll5B?gaF_S_UELHlu zH37?U3ANFPRj^ms_-ZohNd`m^7s6}aPfM3eHH^IU8RcS(We{9R?q2fc=b6QJlo; z;m#{@mOm?cLK4{53to?<)q|3E%I4!9DGVc>j+1u9MPYh<8s(&u)ubjZ+rp5PRQLP+ zxU(|$(=t9*+EKZyV^#JMvuMuHtIKS}Zl2-Na`SOI0;$+jyOgvQl{k$2Ry77wSJ=3k zcZMEd51Frng>hERwohOP*5G)VxnzW?-_9C%-+AVyA4MqWS8T-5<$^}ZI{4~TZ2L)m z;};pRnM@us4~?=n0*3Z#!@%xMI%1GlM}xaYgI8C##(spj)g=779sPIP=l9FD9e1N1 z-?beIo@mso2MyUD>NFtcnQ`g`VEvg6Ai$fH#3^BtSR`LqLsN=}y_YAg9i}{jDL)4S zk3Si*IX$&}8Q#t!5UP>q1kUdy2?ZMSX(J)e(O7e91ohZQ3;5_Q&r zt>bj*YQLP5q(X@UuN%+0vka@V%T=e@PO9t&ChPM($X`DiL>N2W4H0{@Z3?V$#us+6 zG}XRbSjv=pk+efx1_2)Fm?-qHScI5#pwN|MjJPbPdntSn*|lo$;h-U%h|;h>#>rO* z>HX8S&N^IYJk|dt_nb%nG5mN|NvA@md6-y{NJ`Xl%2sCU!FFr!9doOsH2Oc^EHRLz60U?G zUvY)78_78GXKA0k^06xo2jv_2cM)0Rg7OVvRud8o?xK#Ab~HbnD|p8k-1fa$452k3mh=CQ{EM@b*o?#+U*x2T|Y93Ro~&m zuysOhm3ul*N$-`>hZ?gZz~1f|6Td5yzQ7uA}l$t7va6`(2mNt@frm?053+}kObCKnOeRk1*yn6R`5CT~kf3pLC zByxjY8O50I6hQWJrzv*F58?bc<8noXN9+20i(-)XI=7=0*qh5n%(?A|nkeZcC=_ZH zO9R)xKlokVp%G|diJLFL63Reiy`(yOM9v04W&bRbt@%Ui4CE)k0~$bI@|K!Fko;=5 zp#USf|6GH8sNp@wdjgH{&+DEOx~0`|>iXyg_Go{$5gkr1XZVPpb_r=0SQtv1dAB_Hj@VjGEZD@Vqq`U%Uw<96CamLLKnB9>& z!{A$D-lncARb=%^eQ179XLXlgpRmM156`0PRaZ{-?gbsLi|u+wj&|&CqAl0N?p~XEJL)mtn-74(#S`8> zp^%fzTX_H3Lo9*hCGnlen#=-N@winCuoQ1mzuUoV`7Dhw=E)1}=BH?K<}ojo-s*l+ zUUl+}g-K;F)JKI3(1pvytQ`tp^*)pCNV2HbjhWtddjXNf?_WKzmHru*rfhE8>mWqx zuRGEjXEfN8klLF%YRVY%SYE*>IjRf-Lv&7m^T$q&yDKL(o$AgG=_dG;m>zRK6eA*D zD7H6eW;<_0Ci{hwn^7o7uWDBl4uwe=PZ#h{SbvDC%>K)6Pj#A~`*6nk8^LBGFR0k# z7e|iuRx0c`=#QT7f$Ou?9%rpTG>SG(*De(zp#ebhOUvT&lew=zky%0#g;i#i@{Q_+ z-<7`Vn9Kt^NhvZ>r@JPg;ZEkJzz#Rf;_(uaZRSD{1S0%n-y_viZ0JF#loE&e1YgSF zWUwX=s+gXqm_n6SN8K_3`Z#vgXuoV3cMIyRdABy=M=!^Y_IG!6Ajp+%Rm5W$n28U! zClT94=x17Xqp9;`DKafBH7b7e4;WZYSG2 zTO}2z2k?|_c_<4MRK%Gn9iu|NJwH9&9zDJVk$tbimHlp9PZYCt4FC(@@dQOS)wCNG z1}w^acNfQ*S*<0@)U9aFhW%7N9nx#;-IOH8H*FQ+d`-o&LhY~NSwS=m#xzkDTb zG=dq*Sw9>0f-wDxdgEb;uaEYuAjWq-LvG~K`a>wyx%&~xP1$gP7zJNvV8_+i_^XD5 zBY5)~2hyDj>{c@4(aUnS;yxNWNWuQ*4e6fEo7LF^2V{?1UCA#n^@^n?CtKFe93Kml zaw#?#%1IyD6t~dw4vimoMG5z)1yXI(QBqbkwg000aX|BrO%?16ocNz6swMDLcI80a z!WifI<(!Zpk8fJmg==;mY}QjU$&dTRUCtZNL6HZyCkG(fWIegv?2U}tP%j@-zS|B+ zM06 z)JO~dC3be%D6isvvK@JNl0i;xM=L7ou<#=~+q^6lda`v~%uSZB-*BaBuK>q^ zIRp&@*j=jZZgx2s=t(mNf$2Cz7-SCyPY-(HbvU1a9ltSzpRFMOl1$|b`K&EL1?Q>w z!0vEw3CH?s^WElV{K;MhR0WlHQFxlWiXg~EQ~K~qkv~fNXS@R_t?uBKCe55w4Gl}^ zD!IDBjHImS!4{)!;!ksmV#0-)pK2favkHc4rx_$I*QMN1r97kLA~){ zd8cz=;PAOHd>~qyQUubAUxQTsA)x_(E{+88C+Eq!fee{12#)8$XN5;Dfkh|G5Lh^9 z`2K_HrzGMm^rpjOv~8Woy(eI;xBVz=VXiwIAi)mS>^teWuB%LM-n^;TaC!EOdleaF z>Z_@R>kOljwVZdwbQsg4Lzxdx}bE*D`H%d9v( zW-cx+mX^IhWGrA1$SN~7kA@+x9~Ja`l@uO#R7V$FMmmUX-_S3Yn8N$|aH!aFy-urE zL|#60t>d_7G&Cf8P+w8;!de)00tT~HDH=#X(oH^0f9s|`8QOGs2UFN?<8keCTjB={ z+Ln9>uP9}?+D31P`Nsi;$a)U6HA>Ite)ms|ojpC1^$ra&UsE~lUoKb9<8kx#sv;mk z5AJ=c?Rrv&Z*T(uh1)snSZd2D7i5%Zya4cs(BuCYrO=LgL}^zHvY=wSc(9j!70sko zT0jVW)KeQbme#vMqiAYs$`}jq>(?W>Pq{eP>;dkeK@Ty4UsT=v;^H85;xn)uyJ+;4 z_+BW_)2HM0LnV(m8#!8($A^2|xh&dNGV9;hiBTI#-vjdeU^!xy6jamCkqb`p#}~@y1<|$raWIiF$uh?w0mfTi6w4JH(W$7IWwAgFS7mr1C4S- z3QKRYD?x>Ue~qtq9iF7Yj9NQ^C?!3A86(Q_>DOF!QI8Gj&mL8Ag}PFxkB{iVp((~{ zY;505*}sR1S)kq__z+G%BISiku>_TiQb!Lc?}`n2_y8NdZLO%6p_iv#?$QIzDCxxQ zS!1P7liQ7i{m;oWsV{B-SxXb8_qq8ju6dtKnRyTLagRzUrb>#( zq~^dMAp7@v&yz$aCupridJ$4i^I*H6BV_Z#+7AoQuX)t2GY?xm6*M<{ka+d!=0Z+l z%wQ$v-&3MY)>uWqgjj?@(=#=Zzb$ijQdZ z_5kGd8AG{sjY?@r$x$Mra4AG;5Z|`U9L<%JMBsLQhC)BRopH^@3S>JRbm~TGFwaLk zvmxGyzFL&^n4;mXX3tAOOm$x&YH5ui_fS$%(PVF(31oxKOiuntRKQ($8lKSmds>bX zg}|6B6@L{q2Ok|oR`H&=V5tp`jeZxaehq;QE<__JCl>M5%4#bsvoYflKt|w$-}+mh z&DITYiNg*BdgrF6f2Ndb_2bdjr!)LPe4)IZ5BoS!7I_B#y!Vwi)nx@7_!|KZ*ximS zY+PLdXUv{ePh_7D5S+P<8O1IDu=`7I=;o#+1~&ldfHza#g8vx+08s-gf9xpwRAKsK zo8Nj-el{H%y*APjmard7skUGu>7;ob^a^$LG0(*!7boW}QXW~8U7TKDFk|fOeCSL5 zEC=;*Ui+WEqn{!_xqi$FYpmbv4p7Xl#wOzh==D02FPuXoJ((kW`A8sY< zM!?4y$k<8>l#7bZ2;(Xy>tB0$c@gjdTp2qyk8f?q)*Qz629L7mp(rNf0zuAt(0EsU z8jrLK*)zj94NK04Y^Yaqo;uE;9S8A1N8;S~k zfz_1p-o;V@FkO9>9RM``rKDi@*B2nF%M!~>sG*zerGtmKQQn` zR;#q!)PKo>mj=`T2?~6Su;M;KwvbTyRb}3gaP1IBJ#ReOGC{n0P~G!7ih9t)zuMou z>4N{Ps)Nv8zD|3olBSk8#v@MS1jV`6c76_4qIHi6>BSn)ysHpAgL)%*fp9>Yvo6Pv zSkGkUKAuxvxleMnnZ|F(W7zNUxgj4GIe^5O8KazwsGke%MQW79?iaaye~_^O^7J{B zft0fz7tBzeM8V_~CKBwk_J-6b^?2!mIL%WP4|n}rlhC(7LO=&BI}d;TOLQ zywAqrg^41>5O=rN4nous-Pt*yHpGkVYE{lm2_PE0zYq7bBR>Wa1Ra%#;OE5d>(Al0 z(Z3aX`GWxCvATk!Fa-pr{GO1*Q*13zNKW1eS7*~-9Q7R*ukfS$vQqEx9;s`h$Gso7 zVl!8wef8CbjPiAnnoHiJ?{RmPb~mGbc8s)NAdg~WS2t8M%5mNdi6(Ebk#Sau?7(gs zsHL0sW5j*yh3gs_;fb>XwAE`S1axK56ZnARP_qt&IRW$ ztw|j5+DD#16YU}Jx@0dR$eZ+!S7(Qj7$3+z#2R^v9iItdF)ovEujty&l+@!yu0C-$ zg#_U))~MPru8^1*|70L74H;KftY&|nDUHcxl`2uVfXA6oP zj6CEa)Gv*;4z{6EYE3n+k)A>%E6B#~4AUtto3~~8OdpP}UVKjbG-E|YulGsnc4E~| z>9jhH)N`ks^F!|JU)T?eUukb2Of$Bz(ltwXTvhl8+r#t7A+ndGIb_dmp|sptumzLy zt0jXrv6emY&(BR!2G+z|7fKqokc9VTv(RvK!ev^AD>*-m$Xs$M=h#m8A&W?@kHF66 zF2p!5GA4OLcExo>?Gw}CPPqD4A*KEZS1twwS9a??;N8q`Y zC{IX!_Jya|PDeE;k_@@NQNt*qS(YOn8WG+J6J!tk zL_eR@#*@dKyPP$v8{{!7p#?YTbN@3F2GSzdlWxTI4+|n!CAttsbY)Yo@8)8EqAxf5 zK~eS*CPg-iKw`Jw|E603g2X+Jjz1KeC{pFWD<0PD2&LUOMIH4jowKl*^b~z8gMec7P z-GV1)@`t}FH;^82VJ)XP5qDPx8S$P<7&Jpv$yxgN-&+P={sv{X15+c;YDXZKXybA{ zs!b+`KG=CMME&ihE~0-fXXabZc~4g^?k(8-!hqYilGD`$8r%{|l;NX{NR1T6)m0h~ zD~!%f8+=sKH^|{@CAvZYoF+>@1*sJZAMw>{3Pe0 zL;tdR%l`PMIy85uN-mnJ$!jN#)!h?8h+LkR_# zp5-I)AxaUM?`zE&fQKWHfRK+cn4>8{h~4Z{C-x?Wy_I%Q z=>sNq00w>6q!DB%IfkU=X8rnZY?Sv@$+>^K%@TNK-+nmS01}U|k_Hg+9$II3uS9O2 z<^KY#|F3}Rw{x9;Y|HZxkTmGxs|DV;|dhrLh|8KkgUjH}tJ%@R<{E4AMWd6wv z`0MEpr0Aa`Jwy%vq!Rr9+*Hj|6==6}FBJM4m`soAKau4B^RVu1{Q19$4gU>G|Euo* zx8L@G0)&G_&2U&TAll2I#RUNPiK2ztQCgaiGu0R!;|0gdJYo)w`y8YylO9~?(#4dH z=s~-*=)02E#c?&XgrDyBz5H(daB4pG_@57A3egI$SD@HdVYamM~{eUpX6dR z3N--2N+L(uCpl(G2>G;Wx$OZjGfyMTEuzDdPy_+b2CZE>OQ_gaH@%Vf4&4 za8N1rZ_Oyf`)A(q3Y|~Fdf&zLueL9i&)L=bkm)2;PHpKe6vGfP4=b<@hJPBUsKvqD zNu@o#3?Q)iU7?KOHI;Cgjz5m<`Kz+!6cv>rHeG;F=gf6XVq)Su-a^YSWe9!V5hTYn zQX%&zz#HoR5rElHpPXG?Z@Ve}v*@8?jFzJa^j{qTMewFzVofssh&s6U#>Lg*AD zo>GM)kXB#THeJq-hn(s!(+U7DBf6RX1iev2nTSIu0!Oyp(v?+ zxQOAp0yLr~U)BJ+i&>=5FF`cS;jDVXh0ca z)k&Bt+}Cm1S|CMYve9e8|ASD(GB<0FZ8H`*2CU#UC}81hiku6fVe36RNpd>E7qYAv zj-*44RvgNElC7i z{6oM;cz^PjfPU~-%mv=R5RwcY-6XvdXU`R-#;>z_bb0&*2twYUMo_vv`aQC2bJZUW zc?zUlH7Nm+RW?$8!n_G0Dr^SP;R}IZBj}Gz^GA73B%dU{ ze68?`CHBlaqAaNFB(cD}5~1cVkO3;y8bNMlI@C{4&{J$n#QVsBeqP{1OAkA6{J)`fs5~-sG^uGWEiNGMQK!Jqsq`^{O_G z)P0YV;heaBKB}bL2cO#B`&7@_eWE{2H5OXuPlo%?VKmTs)s(qYB5mBnaFi4Z6B7lG zhd|6R^q<=!O`b%)UBb2bg-gL^!AJpMy^=$MKNK$JiN6m{N@m0SXuoE*+tDjKtSNwxz11uj*)5tC8vvi-9Jhd z=~^OyB)_LtJ=M>n{q$LX5v)ck{re>ktm?N)YU}DY1RbjL;ak9(7epaV5mHDYdPV?n zkXm7^Z-~n7J&Hj?`3VY)YV8$&z6jpHb%0`yJxD~n$ZW&Wc;!$?=voJ=gA0dkhaa7!44txaqN86ymZC?Myg_$|W{W8qY945`58*6I?&)xuhsPBb> z^&UHPG_JLYVQ((j-Bw6lm!3tNL8S@oWW!)@MwmhExnBW_B24|asBtncp4IguHgdOF z=z)A9cxl*{zqsSbc;T;aQyI$!(hriI;F~t4rny(NtINww)k0!-I($~z?ug_SY9C`T zDSCFQ-$g$d7jd|{##`qB=mt`LEg6`R|IFibE6n{V`p`n8rhamV1hXk$GWgMJzOx$R zM07jX@I@q?H&t%9%(EyIg(r31m#=D_S6-7eDN1OAP+5|*SbFfi-3W+LTh3@yXtks>Q4`Y6kUYh(Itq|w5!;|) zOrm%C_`o6_Lf^G_c*s}H#1MCMG>Pq;_X6M*ol0N$u})@%eb1?fLS4bNL&w%%KjTD) zz1pc$7aidIfJy9tr;z!Mgb{~SD3j2NVX(g+_l-kmnEvj{%F5Ex$Je6I-Kv_KcixR* z+;b!;f2BGQI5$7PtiFNC$;|UHa<8{ARUhnsq@R^*hyRM=JF+KN`QiS=~pR6xEdx6dKR?mZE{da zBl_O+cNx1BMo#NTZf>GRW2Q^_P)cjJXD%_Kg_+1u!8DuGb&LY}?mOuYxNUr8eagj| z6G@B%OBoT42+z;9CB9N*?PDOWjFA+jT$O^YQntgE1QyyQz`CA-c;LZXPt8lJel^-lpcb=$}6E z0)ro`+IznMy?o~VSw1|I;pI(yW7EZosgIi@=|6=ny2 z{saz|d&Z>NimkMi^h@b(xY?7WuMHJ8`Z?PMI13gmvcEo)CV`WVRnm1S)@ZM+R4YO%>7oj(izm zbC2lj6y%*z( zJ-&B9(SGtQqh5)8OQIyd7lQYzg>H*=H|Tci2mB$49>XqD>S?*De0OoBKdJ#sB4o`f z7y^NyXAP*TkeZM?Sut?;j@a&=5j(U+^(5WpHXqu!H&6Y+THT062yxmvt4dTU!^h3d z-NE@i#Jemp)U)2z(2y4wRybhE*!4@lwyunKmmS3zB-G{L?vCZML zLTznKQ&Yo1!SrszVn-`v&h9Gh^QY{(f7(EET(;P=&QFiFEZLwQ-q44vSpd&qjBeYR zitTAV=Mv^mdW#~~u7sbuxcvoY)4{U$89Hxqu(>JUs9-%M#DFQqUqL|E;EEIfQC9sI zK-$ETLR*4G2Rt!wUdqt!KYtM4Lg`fdv;!@;j__AS90DWvo1~0lo@Df)5Fy*sSLw?D zT~3ChSeNnU5MEUP+A{51^2ftn$$C8av632xtwvb+9%zEA;odk+G8v=B{maPN)K#5b zQrAZ-cZpl*A|oUFDkCbQye4C%#u5*M=8WC^*Iw}W9ew(!(_z6l%C9G$>M1Zp{lNay zK<|hsrI1)sqvZQek~31z{F&Waoqw8OUK0j`fWqe&go;XgM^4=CeLtXz7sj;&C(qxrBMh+G^nx~Ciy_>cqW zI{K097SZApa~ZZz2PU(0X`l$ER^s*RAILb)IU&=i)Y7^l!$BfqR zLw0$|6WQf)%En#vzPGadI3?uFo{D(7EXfhPa4wWXnB+5gVUN-hIBWD?mp&(|?5*#+ zN`eiAv&|7b?tF?Cx)B^X_AXQr|9j?P2&u%EbWI-L_s~1&QO(Z{R#Zu8^>-{E3^mm| z{un@tj$j6bY^!<@emER6FUyYhA;2rSU5EgGyN^N9w6wJN1OymE$|@=uclZ>%Ez~KEe9P;}5uDd^JnR-M9Ro99Jajm<=zfyE7>Du^jG=z7Ta+)?HUm$!2eo zQ#QRv!G7O)EN_c8G+6YWT7gGHx!wSNS-CDFN0GV%>;E`kRAgV0oy~Ne=I!QY<}LFTaoF|s0KQ|oCig}~Sy`)p z)wEDxiXc&|57~!o=7LqS=R;=l(;hs??0y*0{K1mZ1V64Lq$vo<7gJDPQHi!25E1sH zYBx2)K|B6BV&o$xZbDOQRL_NsYM*lyGoixiD1Q=6;{g3GL!TeH3g#;|cBxqeyFK|- zue$Z4FNEzto&<+G)h-I*P=3dNvNoVHyhh=9AbW@ia!xo1on1w-qBD~+sK<3M>Zp$<1{DU_Ofl6tOYULMwVd3+~4R23HRj! z>)KsB=HrU+DE@%!&ROh@y&WF92?7l7|b-q91T;m&o+NcTUo~p%2<}XQp92-*$Z$hxcMJ()gG49a)mcXaD+KA zFPguI$|R;&{Hzz*jXaoap_ysHwn4={<%f0f4JC#>LxU`XaX5!jUqA6j)aSh%qtA;j zziSnyct$Zw)YJPtpL?bdpp~1-exKgad(VZeT%=i9AZ2jh!^T-Gr}0M?GHD_R>2PG2D9RDbX^3*FzPd z3}6b9RJ=@73&-usr8yi!SeA7gnldesl=YVOp28f(v{Rb89kknNUVOxK5*y}bd??yd z@m&K&@F!=8{JKevbV1Jz{S!Jx)~`qL&);5#AcWb&t&J%Jti$&j29Zij8=>dX@)`?z z(q>6U@4CQ7FDE43)$7PF*E%U^q&_0$EMtb4bEIb-uwkQ@;CKcQA?qR`1bc_H63V#J z@dTyg$#8g*4?&>%KMlFLE!_-Xbf70I1J2c?oH;!6-VBj}f8l*?FOaf*;w3kv-u-RPWA9ZSV9oT|zD^W4Os(^9s<}AY`sGI{ zALD3!8-5~9vx-gGF^4O=GFj`rUgE@tDI2a@oGjaAk|3FxZ8r?N0$4UpbH?7T6SNM+ z*O;Fh8CVN}B%UWCA{W1(y}mIEmEZ+eKJF>`T8h3s?2*|m{P14W^1ik|yR+C6C&pw{ z0wLk-Vo{is!ctVY(hvYr@E~X6BSEN{#cFS!95rl^`wmMpnBV=_bBf)^BGT=3)1@B? zi|M*+Sf8$~PN);Prvc{krE7fz9n_j!`PPE@383)K`MTlq46yE5T1@6sE<8*$#+K2| zm#*+L@3rIbm)rt}nh2SY6W&k>Q?S$OI60YrlEeA6`+5n3WTcfuVWcyMDMQNo0+>rN%T1ISv;fH@plO2Jfawqsf9atA`79CIU z&{bVZqP7TWi3{o5))GQKgqT{9xKDhHX^(lYwHxl>zM=R=;Vy$d3|Tkmna<#!dYF1> za8zSkiD9riLnMR%#`^ncsBpfxF}K89!1Qkqmvs4H|kmLRME0Qv9O?7ul|{$BqN zE}K6TIe)JKLYe>D1%Jb$|1#42mH!O~&|f?LJNeh1f6Kqs{#*VZs=2N9-^@M#n*=>q zroW?x?+vs6gI(zQ_eOs!UD^L;75}x#|GM~Zm;PS=hihXQD#s0+R|1AxN|Aa&TC0qYh?Z2=8UH#wde^>wK z`aQX>wMp23sDr%Okg(ToE@{Xj;_MdBqJN*rjaJ3f?4`xMKL#v((M7;7QY$CUeUEo0 zrES^p7f$KoUXWT^Bs`nbsN;DpQvI55ESJ8iP?<9=_lPc73J2o_8+L%CD(_wR!WyF- zCIdwfy}u!yCn4LaO}(Pc&Z#dUkChzuuCVuh^v8P|eRxkw$E|pl|05V)WHeI~0;a;i zc{&1}#N)fa@)6)x{~hJl$egni!DuJ3+64zF=XW%r0aWa7pil4)Md>2DC&V`94n+w% zel!T?#s$Ardw0c}J;L_tHPO`WiI&Mjc}7OOH-Lkg#B+8qN9qaqlChihf&%Li2OiqfXQ5;=dbcd`CrAWPw0ep z0`d1X7bp_U$q1YqEc3cCUHvg|aEJHMCDX>4=roQv2G0c4N{D(b=UPNgx!u46&l#N!Ymhk7Vj7t~d?FI9@ zi3-fVApm^c5SqR$bVM21{nDzMYFjdka4@C!oe(MdojHr=;u(Svn2mruY}+m5P|<%* zo8vg!<~tpY#cNI$_LRXLPE7LNN28lZo1r5X-*?{e8pTwD*=Bw*e0%o<>(dME8%H9p zPVKB?A@V|5F*nD|SN=k>74}Cf%5M(A`dK3uDnp8{Ehr8@mr=U#2%RAS>>un8G`u3-ic95X@VuwA@~MKYghM>{%JID0NMl5 zVUxs8_lXZyg;N?PaSFM{WK_Y5AKA(!ph1dQeKG*%uW%1bkd2-;QZGlF<7aG>Kp+yF zR>Qd?)m6HN{EF86@IM=Z4~Awh$M)+9UY$m~o{0%JW(#TO$LK}ajt~Ner5}pSeE$U+ zCQT1evXC$xm)!@6f%%!ox5IA;0@EfPHUoxT|+ zRWkL(M~~xv)4OWKAy~tM126x%;r+>q6YaP_0fH><3GRyq0tDBD-~^Yj1c!x%0Kr`scV8sP;&O-NdEV#T_nvd_sZ;l>`o5}E zO>OAunf`arOn3i(((&e_6!r_z`xj;@+0lX~y$$b4;IwvTfPK~Txq*8D*fYnESeJ!_ zw~9&QhJlw@i=uNKcqbH(U`Dd{)N9tdG8P3n;{FSnu@7Oy+CTLwebZBeSW1K#T)iAV zbf)u3T@~{Y(>C6*&6xcXdRNt_(;ehS_E-BnK!m!R z43;L3$)9hVO(Q`f?jBzIJp5V5SIgJKh#cMO-7WC#KzE!6-|(Ku;cE0VdcdjlZ(oR= zU;30!@NL9;mjJj8W_<#Pt!{~IsshDYkgi)+AHw8xw4V0(lriDs1R5>bN2bN`d^TZ& zU__SuGJ7rTFO{_pWXQN$$xWWetPtJd{WEBBcC-mpR0xB%i3R9w0#1n)p$tuA@%8pI4ls_YWK+t zX0o6V6ZE-b8vQ7o=0oO4?!%Qojb?mNg4l32FV}WI!@K&L{*pORqDhB0q0Vr3OPd(0 zS!idiLlE;$`-}rZ&+YgY6jT0Fo1qn%oRLXFn=N65;OM;%8T8nns~Sj2W0_MK8KF3n zudalCMVkti83`g~t{Np=Q^_pas%PfJL=NouD1k2yq;x47Vy-o!Zw%Kzg3{?`&vtH< zyhc+}@;#T?28)Y&pI=Ezzu-=2sdzoLt-1T-o-443efIbe5~|Ph6p%mIS$g|!>Poak ztKju7AWqOxYhXnJm&_Y}Q3WKA-@)~HG7v5Oi^ipo%R8ESE2p+6-Z5|QNr!SKC@v>3 zs#(aDU~pL2dIx4p8Vva03b~qjMTe$8c=WZ2oRk_PqoaFX#Pm5EA8x9|i(@2Q=-X28lzK2wsNF)HW9o1L4JLh_^W}T8ng0+i6<3`WO%k6gm zAo%maow)d3^HSt1qVH&>8(|JkmYY5$7z#PDs=@cY5?{6*Go5t)VJ1A1DA6j`bl3^N z`#QJ#wph1#Oz}AQjw5ly^Aa~xwf>@`*i}@Y?YHrrAD@I#w;dfkcZ)IbG#R2lDH2L^ z40;eX1(*8}l41Dk^Xy3i=}p6ULnnK(Y{tJHT_V4%L<64m{3UQ~6Z>6vvimH{Fz$QW9)9^RsBw)g zAMv}cqlm=s*7>ph>Y1haAf<_6Pfce%@#dNHxFY9ZsoAMrWr+V*Ps>enSOw`8!_O~x zheM2&{sS)tq*xt@35<;~UZ_eMt7gY7wBk1t-^J2x35bX6(LZTNM0sra&Uo0#Sfn3Z zi1>(q6zWghJ8koLW-xbsnJyBO7^HDU7KLB+6qv4{p;gzVH3JAp<_k91tcMn06LWzL z;CG=e@TxuW*bNB6PB2FyJNd(|EXO-z%zN~kqK48z>ywg4TPS`;l}pgNuxn2W8v!g0 znf!bMK_+54l*Y|Pd+FK!yYlHB2;flIuvd}(=yZk`+372Pk`u(5i_#qb znL)w%$?T<(f_qcU8xLM<;6e5D_w)>O%s|#QV7(4R@n&~T4GtRpu8u4hnKNwlD(S0{ z-!tndCkX-abPfePF}v8kb7bR5=w_x8ZFsH-8o;Woo?y`fT9-WL!_Mh?tlXFt?PRnw;Esaz3mC=d)}txu2b% z-zd1C{m6kLrlyKYtJBud$u=Nl?Aw7^X#rs6hNM&8MCfsA$9^T%XaRAJYnF@_{#*;< z;JQIq!^Rjhw{BF`bFzb3D$e9CZ|bHAK&s?Px*h>MjKDQzWsB`u1`mEC&gaz-!$y#< zyBp3)P`4+;`mCF^*#SxaXXB!~W#xYt@93ddS*T`a?UY2LIkTdv#qMaI@Bt z1CLMN2^7IH%QtSO)acv0yXV5z9oD(%Wt+ypc>0ZR+fylAxcx-`aA*zBAti8ruhNI!Zv;#7+qoxWP{j+xWwB@(e# zzT;>BIaOnq!a=ct8TNy8Tu9v@{Bof*Rdv^3 zi-{P#OMYXnmAo#!*B`a0{9}}N@f3A|J38&``0gkFuM*pwQaam>LzG}QJ{9BIIUo~M-n$z`K!?0)8E(p z{p-#;Bm*g3qjAuHA9ikjMducprYTdD{lJ7w3aC8Q5rNYc14?i`349OQbKPqU+CIb= zu(14^kVSw!Qw%SI`^seEvbZQqVYB?chzaKUuKxQF%w&cr5tBmb>YZi9!YK)I$j=Dpa!0kCb(gp02Paj7;p_k=# zea8KZa`Gj?av9&`tGD5$8inDYj$e0fg?mNHNm3ivQS#>I$wT|m^QkkbMAOIWj!ZQZ%d%kl0oI5v;q9*Gkx`cDNB48q zo9bU7_sCm9uJq25KIavVO9o&l^hT8Q2s@EUrm;ypdGORva7yO`m5kM_0JaFBw8`u| z8cY16`S*~!wjRV_$i+!6ChjiAu<}!6nzS3LiaOtJ6y(d^r z(*%YxS6wD@0j=&6U!e)(!fkh+Wy(F=B~BWk!MOo^iRT?2VsJZX}P@U9_6q zhkz9LmOHLt=?~4LtDLvQMEHqF( zQ4|pt?mk~NMa6bYX6t)3;w6rmP$lYvmxIq>RO;F=k`dRZq1c}LNg$C%Rhm~ov^*R| z5i7cSetI7o{BC{cg^S0OF5LQ{P(VA>{H*v%n>8xp6)yVYWTnbGOJ)hM*&;kvHI`}* zK=nQ9E426{^vj0_PLB-sWcEK-KVOG6Tw9@?r*3c;f0w?U4unQ^LX(juOt6yMAW$A$ z;yv{el~9$B4B7ewm7Rs0ryh%+El�`JT1d8C)E;Lo4g6i;h7WH- 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 literal 43495 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 Date: Sat, 25 Apr 2026 19:41:55 +0100 Subject: [PATCH 24/24] Update Changelog.md and Readme.md --- Changelog.md | 10 +++++----- Readme.md | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Changelog.md b/Changelog.md index 646b663..6ca59c8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,11 +6,11 @@ - Using the **sort by last modified** keyword, as the name implies, allows you to sort results by last modified. But unlike **recent pages** you can now use it in multiple places, i.e. with **title search**, **scoped search** and **notebook explorer**. Note it must be placed after the aforementioned keywords. - Examples:\ (`#` = **sort by last modified** keyword, `*` = **title search** keyword, `nb:\` = **notebook explorer** keyword, `>` = **scoped search** keyword) - - ```on: #{your search query}``` - - ```on: *#{your search query}``` - - ```on: nb:\PathToItem\#{your search query}``` - - ```on: nb:\PathToItem\>#{your search query}``` - - ```on: nb:\PathToItem\*#{your search query}``` + - ```on #{your search query}``` + - ```on *#{your search query}``` + - ```on nb:\PathToItem\#{your search query}``` + - ```on nb:\PathToItem\>#{your search query}``` + - ```on nb:\PathToItem\*#{your search query}``` - Added a setting to allow users to always open items in a new OneNote window. ([#40](https://github.com/Odotocodot/Flow.Launcher.Plugin.OneNote/issues/40)) ## 3.0.1 - 2026-03-13 diff --git a/Readme.md b/Readme.md index c3d9163..b950d56 100644 --- a/Readme.md +++ b/Readme.md @@ -151,11 +151,11 @@ Can be used with [default search](#default-search), [notebook explorer](#noteboo

Examples

-- ```on: #{your search query}``` -- ```on: *#{your search query}``` -- ```on: nb:\PathToItem\#{your search query}``` -- ```on: nb:\PathToItem\>#{your search query}``` -- ```on: nb:\PathToItem\*#{your search query}``` +- ```on #{your search query}``` +- ```on *#{your search query}``` +- ```on nb:\PathToItem\#{your search query}``` +- ```on nb:\PathToItem\>#{your search query}``` +- ```on nb:\PathToItem\*#{your search query}```