diff --git a/QuickNotes/Community.PowerToys.Run.Plugin.QuickNotes.UnitTests/MainTests.cs b/QuickNotes/Community.PowerToys.Run.Plugin.QuickNotes.UnitTests/MainTests.cs
index 3861bfa..abac019 100644
--- a/QuickNotes/Community.PowerToys.Run.Plugin.QuickNotes.UnitTests/MainTests.cs
+++ b/QuickNotes/Community.PowerToys.Run.Plugin.QuickNotes.UnitTests/MainTests.cs
@@ -1,6 +1,9 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Globalization;
using System.Linq;
+using System.Threading;
using Wox.Plugin;
+using Community.PowerToys.Run.Plugin.QuickNotes.Properties;
namespace Community.PowerToys.Run.Plugin.QuickNotes.UnitTests
{
@@ -31,4 +34,139 @@ public void LoadContextMenus_should_return_results()
Assert.IsNotNull(results.First());
}
}
+
+ [TestClass]
+ public class LocalizationTests
+ {
+ private CultureInfo originalCulture;
+ private CultureInfo originalUICulture;
+
+ [TestInitialize]
+ public void TestInitialize()
+ {
+ originalCulture = Thread.CurrentThread.CurrentCulture;
+ originalUICulture = Thread.CurrentThread.CurrentUICulture;
+ }
+
+ [TestCleanup]
+ public void TestCleanup()
+ {
+ Thread.CurrentThread.CurrentCulture = originalCulture;
+ Thread.CurrentThread.CurrentUICulture = originalUICulture;
+ Resources.Culture = null;
+ }
+
+ [TestMethod]
+ public void Resources_English_PluginName_ShouldBeCorrect()
+ {
+ Resources.Culture = new CultureInfo("en-US");
+ Assert.AreEqual("QuickNotes", Resources.PluginName);
+ }
+
+ [TestMethod]
+ public void Resources_English_PluginDescription_ShouldBeCorrect()
+ {
+ Resources.Culture = new CultureInfo("en-US");
+ Assert.AreEqual("Save, view, manage, search, tag, and pin quick notes", Resources.PluginDescription);
+ }
+
+ [TestMethod]
+ public void Resources_Ukrainian_PluginName_ShouldBeCorrect()
+ {
+ Resources.Culture = new CultureInfo("uk");
+ Assert.AreEqual("Швидкі нотатки", Resources.PluginName);
+ }
+
+ [TestMethod]
+ public void Resources_Ukrainian_PluginDescription_ShouldBeCorrect()
+ {
+ Resources.Culture = new CultureInfo("uk");
+ Assert.AreEqual("Зберігайте, переглядайте, керуйте, шукайте, тегуйте та закріплюйте швидкі нотатки", Resources.PluginDescription);
+ }
+
+ [TestMethod]
+ public void Resources_Ukrainian_CommandHelp_ShouldBeCorrect()
+ {
+ Resources.Culture = new CultureInfo("uk");
+ Assert.AreEqual("Показати довідку з доступними командами ℹ️", Resources.CommandHelp);
+ }
+
+ [TestMethod]
+ public void Resources_Ukrainian_NoteSaved_ShouldBeCorrect()
+ {
+ Resources.Culture = new CultureInfo("uk");
+ Assert.AreEqual("Нотатку збережено", Resources.NoteSaved);
+ }
+
+ [TestMethod]
+ public void Resources_Ukrainian_NoteDeleted_ShouldBeCorrect()
+ {
+ Resources.Culture = new CultureInfo("uk");
+ Assert.AreEqual("Нотатку видалено", Resources.NoteDeleted);
+ }
+
+ [TestMethod]
+ public void Resources_Ukrainian_NotePinned_ShouldBeCorrect()
+ {
+ Resources.Culture = new CultureInfo("uk");
+ Assert.AreEqual("Нотатку закріплено", Resources.NotePinned);
+ }
+
+ [TestMethod]
+ public void Resources_Ukrainian_NoteUnpinned_ShouldBeCorrect()
+ {
+ Resources.Culture = new CultureInfo("uk");
+ Assert.AreEqual("Нотатку відкріплено", Resources.NoteUnpinned);
+ }
+
+ [TestMethod]
+ public void Resources_Ukrainian_Error_ShouldBeCorrect()
+ {
+ Resources.Culture = new CultureInfo("uk");
+ Assert.AreEqual("Помилка", Resources.Error);
+ }
+
+ [TestMethod]
+ public void Resources_Ukrainian_Cancel_ShouldBeCorrect()
+ {
+ Resources.Culture = new CultureInfo("uk");
+ Assert.AreEqual("Скасувати", Resources.Cancel);
+ }
+
+ [TestMethod]
+ public void Resources_ChineseSimplified_PluginName_ShouldBeCorrect()
+ {
+ Resources.Culture = new CultureInfo("zh-CN");
+ Assert.AreEqual("快速笔记", Resources.PluginName);
+ }
+
+ [TestMethod]
+ public void Resources_ChineseSimplified_PluginDescription_ShouldBeCorrect()
+ {
+ Resources.Culture = new CultureInfo("zh-CN");
+ Assert.AreEqual("保存、查看、管理、搜索、标签和置顶快速笔记", Resources.PluginDescription);
+ }
+
+ [TestMethod]
+ public void Resources_AllLocales_ShouldHaveNonEmptyPluginName()
+ {
+ var cultures = new[] { "en-US", "uk", "zh-CN" };
+ foreach (var cultureName in cultures)
+ {
+ Resources.Culture = new CultureInfo(cultureName);
+ Assert.IsFalse(string.IsNullOrEmpty(Resources.PluginName), $"PluginName should not be empty for culture {cultureName}");
+ }
+ }
+
+ [TestMethod]
+ public void Resources_AllLocales_ShouldHaveNonEmptyPluginDescription()
+ {
+ var cultures = new[] { "en-US", "uk", "zh-CN" };
+ foreach (var cultureName in cultures)
+ {
+ Resources.Culture = new CultureInfo(cultureName);
+ Assert.IsFalse(string.IsNullOrEmpty(Resources.PluginDescription), $"PluginDescription should not be empty for culture {cultureName}");
+ }
+ }
+ }
}
diff --git a/QuickNotes/Community.PowerToys.Run.Plugin.QuickNotes/Community.PowerToys.Run.Plugin.QuickNotes.csproj b/QuickNotes/Community.PowerToys.Run.Plugin.QuickNotes/Community.PowerToys.Run.Plugin.QuickNotes.csproj
index 9875499..12a5713 100644
--- a/QuickNotes/Community.PowerToys.Run.Plugin.QuickNotes/Community.PowerToys.Run.Plugin.QuickNotes.csproj
+++ b/QuickNotes/Community.PowerToys.Run.Plugin.QuickNotes/Community.PowerToys.Run.Plugin.QuickNotes.csproj
@@ -18,7 +18,7 @@
Community.PowerToys.Run.Plugin.QuickNotes
Community.PowerToys.Run.Plugin.QuickNotes
- 1.0.11
+ 1.0.12
false
false
true
@@ -32,6 +32,30 @@
+
+
+
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ Resources.resx
+
+
+ Resources.resx
+
+
+ True
+ True
+ Resources.resx
+
+
+
diff --git a/QuickNotes/Community.PowerToys.Run.Plugin.QuickNotes/Main.cs b/QuickNotes/Community.PowerToys.Run.Plugin.QuickNotes/Main.cs
index 743ede5..1a1cbec 100644
--- a/QuickNotes/Community.PowerToys.Run.Plugin.QuickNotes/Main.cs
+++ b/QuickNotes/Community.PowerToys.Run.Plugin.QuickNotes/Main.cs
@@ -13,6 +13,7 @@
using Wox.Plugin.Logger;
using Microsoft.VisualBasic; // For InputBox
using Microsoft.PowerToys.Settings.UI.Library;
+using Community.PowerToys.Run.Plugin.QuickNotes.Properties;
namespace Community.PowerToys.Run.Plugin.QuickNotes
{
@@ -101,8 +102,8 @@ public class Main : IPlugin, IContextMenu, IPluginI18n, IDisposable, IDelayedExe
private const string NotesFolderPathKey = "NotesFolderPath";
// --- Properties ---
- public string Name => "QuickNotes";
- public string Description => "Save, view, manage, search, tag, and pin quick notes";
+ public string Name => Resources.PluginName;
+ public string Description => Resources.PluginDescription;
private PluginInitContext? Context { get; set; }
private string? IconPath { get; set; }
@@ -145,27 +146,27 @@ public class Main : IPlugin, IContextMenu, IPluginI18n, IDisposable, IDelayedExe
};
// Опис команд
- private readonly Dictionary _commandDescriptions = new Dictionary
+ private Dictionary CommandDescriptions => new Dictionary
{
- { "help", "Show help with available commands ℹ️" },
- { "backup", "Create a backup of notes 💾" },
- { "export", "Export notes to a file 💾" },
- { "edit", "Edit note by number 📝" },
- { "view", "View note details 👁️" },
- { "delall", "Delete all notes 💣" },
- { "del", "Delete note by number 🗑️" },
- { "delete", "Delete note by number 🗑️" },
- { "search", "Search notes by text 🔍" },
- { "searchtag", "Search notes by tag 🏷️" },
- { "pin", "Pin note to top of list 📌" },
- { "unpin", "Unpin note 📎" },
- { "undo", "Undo last deletion ↩️" },
- { "sort", "Sort notes by date or text 🔄" },
- { "tagstyle", "Change tag display style (bold/italic) ✨" },
- { "markdown", "Create multi-line markdown note 📝" },
- { "md", "Create multi-line markdown note 📝" },
- { "sync", "Sync notes to Git repository ☁️" },
- { "restore", "Restore notes from Git repository ☁️" }
+ { "help", Resources.CommandHelp },
+ { "backup", Resources.CommandBackup },
+ { "export", Resources.CommandExport },
+ { "edit", Resources.CommandEdit },
+ { "view", Resources.CommandView },
+ { "delall", Resources.CommandDelAll },
+ { "del", Resources.CommandDel },
+ { "delete", Resources.CommandDel },
+ { "search", Resources.CommandSearch },
+ { "searchtag", Resources.CommandSearchTag },
+ { "pin", Resources.CommandPin },
+ { "unpin", Resources.CommandUnpin },
+ { "undo", Resources.CommandUndo },
+ { "sort", Resources.CommandSort },
+ { "tagstyle", Resources.CommandTagStyle },
+ { "markdown", Resources.CommandMarkdown },
+ { "md", Resources.CommandMarkdown },
+ { "sync", Resources.CommandSync },
+ { "restore", Resources.CommandRestore }
};
// Setting keys
@@ -188,49 +189,49 @@ public Control CreateSettingPanel()
{
PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Textbox,
Key = NotesFolderPathKey,
- DisplayLabel = "Notes folder path",
- DisplayDescription = "Leave empty to use the default AppData location. The folder will be created if it does not exist.",
+ DisplayLabel = Resources.SettingsNotesFolderPath,
+ DisplayDescription = Resources.SettingsNotesFolderPathDesc,
TextBoxMaxLength = 500,
- PlaceholderText = @"Example: C:\Users\User\Documents\QuickNotes",
+ PlaceholderText = Resources.SettingsNotesFolderPathPlaceholder,
},
new()
{
PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Checkbox,
Key = EnableGitSyncKey,
- DisplayLabel = "Enable Git Sync",
- DisplayDescription = "Enable automatic synchronization with Git repository",
+ DisplayLabel = Resources.SettingsEnableGitSync,
+ DisplayDescription = Resources.SettingsEnableGitSyncDesc,
Value = false,
},
new()
{
PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Textbox,
Key = GitRepositoryUrlKey,
- DisplayLabel = "Git Repository URL",
- DisplayDescription = "Example: https://github.com/username/notes.git",
+ DisplayLabel = Resources.SettingsGitRepositoryUrl,
+ DisplayDescription = Resources.SettingsGitRepositoryUrlDesc,
TextBoxMaxLength = 500,
},
new()
{
PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Textbox,
Key = GitBranchKey,
- DisplayLabel = "Git Branch",
- DisplayDescription = "Branch name (default: main)",
+ DisplayLabel = Resources.SettingsGitBranch,
+ DisplayDescription = Resources.SettingsGitBranchDesc,
TextBoxMaxLength = 100,
},
new()
{
PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Textbox,
Key = GitUsernameKey,
- DisplayLabel = "Git Username (optional)",
- DisplayDescription = "Uses global git config if empty",
+ DisplayLabel = Resources.SettingsGitUsername,
+ DisplayDescription = Resources.SettingsGitUsernameDesc,
TextBoxMaxLength = 100,
},
new()
{
PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Textbox,
Key = GitEmailKey,
- DisplayLabel = "Git Email (optional)",
- DisplayDescription = "Uses global git config if empty",
+ DisplayLabel = Resources.SettingsGitEmail,
+ DisplayDescription = Resources.SettingsGitEmailDesc,
TextBoxMaxLength = 200,
},
];
@@ -410,7 +411,7 @@ public List GetQuerySuggestions(Query query, bool execute)
suggestions.Add(new Result
{
Title = cmd,
- SubTitle = _commandDescriptions.ContainsKey(cmd) ? _commandDescriptions[cmd] : $"Execute command '{cmd}'",
+ SubTitle = CommandDescriptions.ContainsKey(cmd) ? CommandDescriptions[cmd] : string.Format(Resources.ExecuteCommand, cmd),
IcoPath = IconPath,
Score = 1000,
Action = _ =>
@@ -435,8 +436,8 @@ public List GetQuerySuggestions(Query query, bool execute)
{
suggestions.Add(new Result
{
- Title = $"Add note: {searchText}",
- SubTitle = "Press Enter to save this note (with timestamp)",
+ Title = string.Format(Resources.AddNote, searchText),
+ SubTitle = Resources.AddNoteSubtitle,
IcoPath = IconPath,
Score = 10,
Action = _ =>
@@ -444,7 +445,7 @@ public List GetQuerySuggestions(Query query, bool execute)
if (execute)
{
CreateNote(searchText);
- Context?.API.ShowMsg("Note saved", $"Saved: {searchText}");
+ Context?.API.ShowMsg(Resources.NoteSaved, string.Format(Resources.NoteSavedDesc, searchText));
return true;
}
return false;
@@ -476,7 +477,7 @@ private string CleanupQuery(string query)
public List Query(Query query)
{
if (!_isInitialized)
- return ErrorResult("QuickNotes not initialized", "Plugin not initialized properly. Please restart PowerToys.");
+ return ErrorResult(Resources.NotInitialized, Resources.NotInitializedDesc);
var originalSearch = query.Search?.Trim() ?? string.Empty;
var searchText = CleanupQuery(originalSearch);
@@ -494,8 +495,8 @@ public List Query(Query query)
{
new Result
{
- Title = "Duplicate 'qq' detected",
- SubTitle = $"Using '{searchText}' instead. No need to type 'qq' twice.",
+ Title = Resources.DuplicateQQDetected,
+ SubTitle = string.Format(Resources.DuplicateQQDetectedDesc, searchText),
IcoPath = IconPath,
Score = 5000,
Action = _ => false
@@ -519,7 +520,7 @@ public List Query(Query query)
results.Add(new Result
{
Title = cmd,
- SubTitle = _commandDescriptions.ContainsKey(cmd) ? _commandDescriptions[cmd] : $"Execute command '{cmd}'",
+ SubTitle = CommandDescriptions.ContainsKey(cmd) ? CommandDescriptions[cmd] : string.Format(Resources.ExecuteCommand, cmd),
IcoPath = IconPath,
Score = 1000,
Action = _ =>
@@ -531,13 +532,14 @@ public List Query(Query query)
}
results.Add(new Result
{
- Title = $"Add note: {searchText}",
- SubTitle = "Press Enter to save this note (with timestamp)",
+ Title = string.Format(Resources.AddNote, searchText),
+ SubTitle = Resources.AddNoteSubtitle,
IcoPath = IconPath,
Score = 50,
Action = _ =>
{
CreateNote(searchText);
+ Context?.API.ShowMsg(Resources.NoteSaved, string.Format(Resources.NoteSavedDesc, searchText));
return true;
}
});
@@ -606,17 +608,12 @@ private List SyncNotes()
{
new Result
{
- Title = "⚠️ Git Sync is not enabled",
- SubTitle = "Enable it in PowerToys Settings → PowerToys Run → Plugins → QuickNotes",
+ Title = Resources.GitSyncNotEnabled,
+ SubTitle = Resources.GitSyncNotEnabledSubtitle,
IcoPath = IconPath,
Action = _ =>
{
- Context?.API.ShowMsg("Git Sync Disabled",
- "Go to PowerToys Settings → PowerToys Run → Plugins → QuickNotes → Additional Options\n\n" +
- "1. Check 'Enable Git Sync'\n" +
- "2. Set your Git Repository URL\n" +
- "3. Configure branch (default: main)\n" +
- "4. Optionally set username and email");
+ Context?.API.ShowMsg(Resources.GitSyncDisabled, Resources.GitSyncDisabledInstructions);
return true;
}
}
@@ -629,16 +626,12 @@ private List SyncNotes()
{
new Result
{
- Title = "⚠️ Git Repository not configured",
- SubTitle = "Click to see setup instructions",
+ Title = Resources.GitRepoNotConfigured,
+ SubTitle = Resources.GitRepoNotConfiguredSubtitle,
IcoPath = IconPath,
Action = _ =>
{
- Context?.API.ShowMsg("Configure Git Repository",
- "Go to PowerToys Settings → QuickNotes → Additional Options\n\n" +
- "Set your repository URL:\n" +
- "• HTTPS: https://github.com/username/notes.git\n" +
- "• SSH: git@github.com:username/notes.git");
+ Context?.API.ShowMsg(Resources.ConfigureGitRepo, Resources.ConfigureGitRepoInstructions);
return true;
}
}
@@ -656,7 +649,7 @@ private List SyncNotes()
if (_gitSyncService == null)
{
- return SingleInfoResult("❌ Git Sync Error", "Could not initialize Git Sync service.");
+ return SingleInfoResult(Resources.GitSyncError, Resources.GitSyncErrorDesc);
}
// Return a result that will trigger sync when user presses Enter
@@ -664,8 +657,8 @@ private List SyncNotes()
{
new Result
{
- Title = "☁️ Sync notes to Git repository",
- SubTitle = $"Repository: {_settings.GitRepositoryUrl} | Branch: {_settings.GitBranch} | Press Enter to sync",
+ Title = Resources.SyncNotesToGit,
+ SubTitle = string.Format(Resources.SyncNotesToGitSubtitle, _settings.GitRepositoryUrl, _settings.GitBranch),
IcoPath = IconPath,
Action = _ =>
{
@@ -686,12 +679,12 @@ private List SyncNotes()
var progressDetail = string.Join("\n", progressMessages);
if (progressMessages.Count > 0)
{
- Context?.API.ShowMsg(success ? "✅ Sync Completed" : "❌ Sync Failed",
- $"{message}\n\nDetails:\n{progressDetail}");
+ Context?.API.ShowMsg(success ? Resources.SyncCompleted : Resources.SyncFailed,
+ $"{message}\n\n{Resources.Details}\n{progressDetail}");
}
else
{
- Context?.API.ShowMsg(success ? "✅ Sync Completed" : "❌ Sync Failed", message);
+ Context?.API.ShowMsg(success ? Resources.SyncCompleted : Resources.SyncFailed, message);
}
});
}
@@ -714,17 +707,12 @@ private List RestoreNotes()
{
new Result
{
- Title = "⚠️ Git Sync is not enabled",
- SubTitle = "Enable it in PowerToys Settings → PowerToys Run → Plugins → QuickNotes",
+ Title = Resources.GitSyncNotEnabled,
+ SubTitle = Resources.GitSyncNotEnabledSubtitle,
IcoPath = IconPath,
Action = _ =>
{
- Context?.API.ShowMsg("Git Sync Disabled",
- "Go to PowerToys Settings → PowerToys Run → Plugins → QuickNotes → Additional Options\n\n" +
- "1. Check 'Enable Git Sync'\n" +
- "2. Set your Git Repository URL\n" +
- "3. Configure branch (default: main)\n" +
- "4. Optionally set username and email");
+ Context?.API.ShowMsg(Resources.GitSyncDisabled, Resources.GitSyncDisabledInstructions);
return true;
}
}
@@ -737,16 +725,12 @@ private List RestoreNotes()
{
new Result
{
- Title = "⚠️ Git Repository not configured",
- SubTitle = "Click to see setup instructions",
+ Title = Resources.GitRepoNotConfigured,
+ SubTitle = Resources.GitRepoNotConfiguredSubtitle,
IcoPath = IconPath,
Action = _ =>
{
- Context?.API.ShowMsg("Configure Git Repository",
- "Go to PowerToys Settings → QuickNotes → Additional Options\n\n" +
- "Set your repository URL:\n" +
- "• HTTPS: https://github.com/username/notes.git\n" +
- "• SSH: git@github.com:username/notes.git");
+ Context?.API.ShowMsg(Resources.ConfigureGitRepo, Resources.ConfigureGitRepoInstructions);
return true;
}
}
@@ -764,7 +748,7 @@ private List RestoreNotes()
if (_gitSyncService == null)
{
- return SingleInfoResult("❌ Git Sync Error", "Could not initialize Git Sync service.");
+ return SingleInfoResult(Resources.GitSyncError, Resources.GitSyncErrorDesc);
}
// Show warning message
@@ -772,8 +756,8 @@ private List RestoreNotes()
{
new Result
{
- Title = "⚠️ Restore notes from Git repository?",
- SubTitle = $"Repository: {_settings.GitRepositoryUrl} | Branch: {_settings.GitBranch} | This will replace your local notes. Press Enter to continue.",
+ Title = Resources.RestoreFromGit,
+ SubTitle = string.Format(Resources.RestoreFromGitSubtitle, _settings.GitRepositoryUrl, _settings.GitBranch),
IcoPath = IconPath,
Action = _ =>
{
@@ -794,12 +778,12 @@ private List RestoreNotes()
var progressDetail = string.Join("\n", progressMessages);
if (progressMessages.Count > 0)
{
- Context?.API.ShowMsg(success ? "✅ Restore Completed" : "❌ Restore Failed",
- $"{message}\n\nDetails:\n{progressDetail}");
+ Context?.API.ShowMsg(success ? Resources.RestoreCompleted : Resources.RestoreFailed,
+ $"{message}\n\n{Resources.Details}\n{progressDetail}");
}
else
{
- Context?.API.ShowMsg(success ? "✅ Restore Completed" : "❌ Restore Failed", message);
+ Context?.API.ShowMsg(success ? Resources.RestoreCompleted : Resources.RestoreFailed, message);
}
});
}
@@ -841,11 +825,11 @@ private void CreateNote(string note)
}
catch (IOException ex) when ((ex.HResult & 0x0000FFFF) == 32)
{
- Context?.API.ShowMsg("File in use", "Notes file is used by another process. Please try again shortly.");
+ Context?.API.ShowMsg(Resources.FileInUse, Resources.FileInUseDesc);
}
catch (Exception ex)
{
- Context?.API.ShowMsg("Error creating note", ex.Message);
+ Context?.API.ShowMsg(Resources.ErrorCreatingNote, ex.Message);
}
}
@@ -869,8 +853,8 @@ private List CreateMarkdownNote(string initialText = "")
{
new Result
{
- Title = "Create Markdown Note",
- SubTitle = "Open multi-line editor for markdown notes with live preview",
+ Title = Resources.CreateMarkdownNote,
+ SubTitle = Resources.CreateMarkdownNoteSubtitle,
IcoPath = IconPath,
Score = 1000,
Action = _ =>
@@ -883,7 +867,7 @@ private List CreateMarkdownNote(string initialText = "")
if (result == true && !string.IsNullOrWhiteSpace(dialog.ResultText))
{
CreateNote(dialog.ResultText);
- Context?.API.ShowMsg("Markdown Note Saved", "Your multi-line markdown note has been saved successfully!");
+ Context?.API.ShowMsg(Resources.MarkdownNoteSaved, Resources.MarkdownNoteSavedDesc);
Context?.API.ChangeQuery("qq", true); // Refresh the notes list
}
@@ -891,7 +875,7 @@ private List CreateMarkdownNote(string initialText = "")
}
catch (Exception ex)
{
- Context?.API.ShowMsg("Error", $"Failed to open markdown editor: {ex.Message}");
+ Context?.API.ShowMsg(Resources.Error, string.Format(Resources.FailedToOpenMarkdownEditor, ex.Message));
return false;
}
}
@@ -908,8 +892,8 @@ private List AddNoteCommand(string noteText)
{
new Result
{
- Title = $"Add note: {noteText}",
- SubTitle = "Press Enter to save this note (with timestamp)",
+ Title = string.Format(Resources.AddNote, noteText),
+ SubTitle = Resources.AddNoteSubtitle,
IcoPath = IconPath,
Action = _ =>
{
@@ -924,7 +908,7 @@ private List AddNoteCommand(string noteText)
private List SearchNotes(string searchTerm)
{
if (string.IsNullOrWhiteSpace(searchTerm))
- return SingleInfoResult("Search QuickNotes", "Usage: qq search ");
+ return SingleInfoResult(Resources.SearchQuickNotes, Resources.SearchUsage);
var notes = ReadNotes();
var matches = notes
@@ -932,15 +916,13 @@ private List SearchNotes(string searchTerm)
.ToList();
if (!matches.Any())
- return SingleInfoResult("No matches found", $"No notes contain '{searchTerm}'.");
+ return SingleInfoResult(Resources.NoMatchesFound, string.Format(Resources.NoNotesContain, searchTerm));
var results = new List();
foreach (var match in matches)
{
var highlighted = HighlightMatch(match.Text, searchTerm);
- results.Add(CreateNoteResult(match,
- $"Press Enter to copy | Shift+Enter for content only | Ctrl+Click to Edit",
- highlighted));
+ results.Add(CreateNoteResult(match, Resources.SearchNoteSubtitle, highlighted));
}
return results;
}
@@ -954,7 +936,7 @@ private string HighlightMatch(string noteText, string searchTerm)
private List SearchTag(string tag)
{
if (string.IsNullOrWhiteSpace(tag))
- return SingleInfoResult("Search by Tag", "Usage: qq searchtag (e.g., qq searchtag work)");
+ return SingleInfoResult(Resources.SearchByTag, Resources.SearchByTagUsage);
var tagSearch = tag.StartsWith('#') ? tag : "#" + tag;
var notes = ReadNotes();
@@ -963,13 +945,13 @@ private List SearchTag(string tag)
.ToList();
if (!matches.Any())
- return SingleInfoResult("No matches found", $"No notes found with tag '{tagSearch}'.");
+ return SingleInfoResult(Resources.NoMatchesFound, string.Format(Resources.NoNotesWithTag, tagSearch));
var results = new List();
foreach (var match in matches)
{
var highlighted = HighlightMatch(match.Text, tagSearch);
- results.Add(CreateNoteResult(match, $"Found note with tag '{tagSearch}'. Enter to copy.", highlighted));
+ results.Add(CreateNoteResult(match, string.Format(Resources.FoundNoteWithTag, tagSearch), highlighted));
}
return results;
}
@@ -1018,16 +1000,16 @@ private List ToggleTagStyle(string style)
if (style.Equals("bold", StringComparison.OrdinalIgnoreCase))
{
_useItalicForTags = false;
- return SingleInfoResult("Tag style set to bold", "Tags will now appear as 【#tag】", true);
+ return SingleInfoResult(Resources.TagStyleSetToBold, Resources.TagStyleBoldDesc, true);
}
else if (style.Equals("italic", StringComparison.OrdinalIgnoreCase))
{
_useItalicForTags = true;
- return SingleInfoResult("Tag style set to italic", "Tags will now appear as 〈#tag〉", true);
+ return SingleInfoResult(Resources.TagStyleSetToItalic, Resources.TagStyleItalicDesc, true);
}
else
{
- return SingleInfoResult("Invalid tag style", "Use 'qq tagstyle bold' or 'qq tagstyle italic'");
+ return SingleInfoResult(Resources.InvalidTagStyle, Resources.InvalidTagStyleDesc);
}
}
@@ -1048,7 +1030,7 @@ private List GetInstructionsAndNotes(string? currentSearch)
if (pinned.Any())
{
- results.Add(new Result { Title = "--- Pinned Notes ---", IcoPath = IconPath, Action = _ => false });
+ results.Add(new Result { Title = Resources.PinnedNotes, IcoPath = IconPath, Action = _ => false });
foreach (var note in pinned)
{
var highlighted = string.IsNullOrWhiteSpace(currentSearch)
@@ -1056,7 +1038,7 @@ private List GetInstructionsAndNotes(string? currentSearch)
: HighlightMatch(note.Text, currentSearch);
results.Add(CreateNoteResult(note,
- $"Pinned | Enter to copy clean content (no timestamp/tags) | Ctrl+Click to Edit | qq unpin {note.DisplayIndex}",
+ string.Format(Resources.PinnedNoteSubtitle, note.DisplayIndex),
highlighted));
}
}
@@ -1064,7 +1046,7 @@ private List GetInstructionsAndNotes(string? currentSearch)
if (regular.Any())
{
if (pinned.Any())
- results.Add(new Result { Title = "--- Notes ---", IcoPath = IconPath, Action = _ => false });
+ results.Add(new Result { Title = Resources.Notes, IcoPath = IconPath, Action = _ => false });
foreach (var note in regular)
{
@@ -1072,18 +1054,16 @@ private List GetInstructionsAndNotes(string? currentSearch)
? note.Text
: HighlightMatch(note.Text, currentSearch);
- results.Add(CreateNoteResult(note,
- $"Press Enter to copy without timestamp | Ctrl+C for full note | Ctrl+Click to Edit",
- highlighted));
+ results.Add(CreateNoteResult(note, Resources.NoteSubtitle, highlighted));
}
}
if (!pinned.Any() && !regular.Any())
{
if (string.IsNullOrEmpty(currentSearch))
- results.Add(SingleInfoResult("No notes found", "Type 'qq ' to add one, or 'qq help' for commands.").First());
+ results.Add(SingleInfoResult(Resources.NoNotesFound, Resources.NoNotesFoundDesc).First());
else
- results.Add(SingleInfoResult($"No notes match '{currentSearch}'", "Try a different search or add a new note.").First());
+ results.Add(SingleInfoResult(string.Format(Resources.NoNotesMatch, currentSearch), Resources.NoNotesMatchDesc).First());
}
if (!string.IsNullOrWhiteSpace(currentSearch))
@@ -1116,8 +1096,10 @@ private Result CreateNoteResult(NoteEntry note, string subTitle, string? display
SubTitle = subTitle,
IcoPath = IconPath,
ToolTipData = new ToolTipData(
- "Note Details",
- $"ID: {note.Id}\nDisplay Index: {note.DisplayIndex}\nPinned: {note.IsPinned}\nCreated: {(note.Timestamp != DateTime.MinValue ? note.Timestamp.ToString("g") : "Unknown")}\nText: {DecodeMultiLineNote(note.Text)}\n\nTip: Right-click for copy options or edit."
+ Resources.NoteDetails,
+ string.Format(Resources.NoteDetailsTooltip, note.Id, note.DisplayIndex, note.IsPinned,
+ note.Timestamp != DateTime.MinValue ? note.Timestamp.ToString("g") : Resources.Unknown,
+ DecodeMultiLineNote(note.Text))
),
ContextData = note,
Action = customAction ?? (c =>
@@ -1130,7 +1112,7 @@ private Result CreateNoteResult(NoteEntry note, string subTitle, string? display
}
catch (Exception ex)
{
- Context?.API.ShowMsg("Error", $"Failed to copy note: {ex.Message}");
+ Context?.API.ShowMsg(Resources.Error, string.Format(Resources.FailedToCopyNote, ex.Message));
return false;
}
})
@@ -1164,8 +1146,8 @@ private List DeleteNote(string indexOrText, bool confirmed = false)
if (noteToDelete.entry == null)
{
var available = notes.Select(n => n.entry.DisplayIndex).OrderBy(x => x).ToList();
- return SingleInfoResult("Note not found",
- $"Note number {targetIndex} does not exist.\nAvailable: {string.Join(", ", available)}");
+ return SingleInfoResult(Resources.NoteNotFound,
+ string.Format(Resources.NoteNotFoundDesc, targetIndex, string.Join(", ", available)));
}
// Якщо не підтверджено - показуємо запит на підтвердження
@@ -1175,8 +1157,8 @@ private List DeleteNote(string indexOrText, bool confirmed = false)
{
new Result
{
- Title = "Confirm Deletion",
- SubTitle = $"Delete note #{targetIndex}?\n{Truncate(StripTimestamp(noteToDelete.entry.Text), 100)}\nPress Enter to confirm",
+ Title = Resources.ConfirmDeletion,
+ SubTitle = string.Format(Resources.ConfirmDeleteNote, targetIndex, Truncate(StripTimestamp(noteToDelete.entry.Text), 100)),
IcoPath = IconPath,
Score = 1000,
Action = _ =>
@@ -1189,8 +1171,8 @@ private List DeleteNote(string indexOrText, bool confirmed = false)
},
new Result
{
- Title = "Cancel",
- SubTitle = "Keep this note",
+ Title = Resources.Cancel,
+ SubTitle = Resources.KeepThisNote,
IcoPath = IconPath,
Score = 999,
Action = _ => true
@@ -1220,8 +1202,8 @@ private List DeleteNote(string indexOrText, bool confirmed = false)
{
new Result
{
- Title = "Confirm Deletion",
- SubTitle = $"Delete note #{exactMatches[0].entry.DisplayIndex}?\n{Truncate(StripTimestamp(exactMatches[0].entry.Text), 100)}\nPress Enter to confirm",
+ Title = Resources.ConfirmDeletion,
+ SubTitle = string.Format(Resources.ConfirmDeleteNote, exactMatches[0].entry.DisplayIndex, Truncate(StripTimestamp(exactMatches[0].entry.Text), 100)),
IcoPath = IconPath,
Score = 1000,
Action = _ =>
@@ -1233,8 +1215,8 @@ private List DeleteNote(string indexOrText, bool confirmed = false)
},
new Result
{
- Title = "Cancel",
- SubTitle = "Keep this note",
+ Title = Resources.Cancel,
+ SubTitle = Resources.KeepThisNote,
IcoPath = IconPath,
Action = _ => true
}
@@ -1250,8 +1232,8 @@ private List DeleteNote(string indexOrText, bool confirmed = false)
{
new Result
{
- Title = "Multiple matching notes found",
- SubTitle = "Please select a specific note to delete:",
+ Title = Resources.MultipleMatchingNotes,
+ SubTitle = Resources.SelectNoteToDelete,
IcoPath = IconPath,
Score = 1000
}
@@ -1261,8 +1243,8 @@ private List DeleteNote(string indexOrText, bool confirmed = false)
{
results.Add(new Result
{
- Title = $"Delete #{match.entry.DisplayIndex}: {Truncate(StripTimestamp(match.entry.Text), 70)}",
- SubTitle = match.entry.IsPinned ? "[PINNED] Press Enter to delete" : "Press Enter to delete",
+ Title = string.Format(Resources.DeleteNoteNumber, match.entry.DisplayIndex, Truncate(StripTimestamp(match.entry.Text), 70)),
+ SubTitle = match.entry.IsPinned ? Resources.PinnedPressEnterToDelete : Resources.PressEnterToDelete,
IcoPath = IconPath,
Score = 900 - match.entry.DisplayIndex,
Action = _ =>
@@ -1291,8 +1273,8 @@ private List DeleteNote(string indexOrText, bool confirmed = false)
{
new Result
{
- Title = "Confirm Deletion",
- SubTitle = $"Delete note #{partialMatches[0].entry.DisplayIndex}?\n{Truncate(StripTimestamp(partialMatches[0].entry.Text), 100)}\nPress Enter to confirm",
+ Title = Resources.ConfirmDeletion,
+ SubTitle = string.Format(Resources.ConfirmDeleteNote, partialMatches[0].entry.DisplayIndex, Truncate(StripTimestamp(partialMatches[0].entry.Text), 100)),
IcoPath = IconPath,
Score = 1000,
Action = _ =>
@@ -1304,8 +1286,8 @@ private List DeleteNote(string indexOrText, bool confirmed = false)
},
new Result
{
- Title = "Cancel",
- SubTitle = "Keep this note",
+ Title = Resources.Cancel,
+ SubTitle = Resources.KeepThisNote,
IcoPath = IconPath,
Action = _ => true
}
@@ -1321,8 +1303,8 @@ private List DeleteNote(string indexOrText, bool confirmed = false)
{
new Result
{
- Title = "Multiple partial matching notes found",
- SubTitle = "Please select a specific note to delete:",
+ Title = Resources.MultiplePartialMatchingNotes,
+ SubTitle = Resources.SelectNoteToDelete,
IcoPath = IconPath,
Score = 1000
}
@@ -1332,8 +1314,8 @@ private List DeleteNote(string indexOrText, bool confirmed = false)
{
results.Add(new Result
{
- Title = $"Delete #{match.entry.DisplayIndex}: {Truncate(StripTimestamp(match.entry.Text), 70)}",
- SubTitle = match.entry.IsPinned ? "[PINNED] Press Enter to delete" : "Press Enter to delete",
+ Title = string.Format(Resources.DeleteNoteNumber, match.entry.DisplayIndex, Truncate(StripTimestamp(match.entry.Text), 70)),
+ SubTitle = match.entry.IsPinned ? Resources.PinnedPressEnterToDelete : Resources.PressEnterToDelete,
IcoPath = IconPath,
Score = 900 - match.entry.DisplayIndex,
Action = _ =>
@@ -1348,7 +1330,7 @@ private List DeleteNote(string indexOrText, bool confirmed = false)
}
else
{
- return SingleInfoResult("Note not found", $"No note with content '{indexOrText}' was found. Try using the note number instead.");
+ return SingleInfoResult(Resources.NoteNotFound, string.Format(Resources.NoteNotFoundByContent, indexOrText));
}
}
}
@@ -1366,7 +1348,7 @@ private List DeleteSpecificLine(List rawLines, NoteEntry noteToR
var lineToRemove = rawLines.FirstOrDefault(line => line.StartsWith(idPrefix));
if (lineToRemove == null)
{
- return ErrorResult("Note not found", "The note was not found in the file. It may have been deleted already.");
+ return ErrorResult(Resources.NoteNotFound, Resources.NoteNotFoundInFile);
}
// Зберігаємо для Undo
@@ -1378,13 +1360,13 @@ private List DeleteSpecificLine(List rawLines, NoteEntry noteToR
// Показуємо користувачу короткий текст нотатки
var displayText = StripTimestamp(noteToRemove.Text);
- return SingleInfoResult("Note deleted",
- $"Removed note #{noteToRemove.DisplayIndex}: {Truncate(displayText, 60)}\nTip: Use 'qq undo' to restore the note.", true);
+ return SingleInfoResult(Resources.NoteDeleted,
+ string.Format(Resources.NoteDeletedDesc, noteToRemove.DisplayIndex, Truncate(displayText, 60)), true);
}
catch (Exception ex)
{
- return ErrorResult("Error deleting note",
- $"Could not delete note: {ex.Message}\nPlease try again or restart PowerToys Run.");
+ return ErrorResult(Resources.ErrorDeletingNote,
+ string.Format(Resources.ErrorDeletingNoteDesc, ex.Message));
}
}
@@ -1399,7 +1381,7 @@ private List PinNote(string indexStr, bool pin)
if (noteToUpdate == null)
{
var available = notes.Select(n => n.DisplayIndex).OrderBy(x => x).ToList();
- return SingleInfoResult("Note not found", $"Note #{displayIndex} does not exist. Available: {string.Join(", ", available)}");
+ return SingleInfoResult(Resources.NoteNotFound, string.Format(Resources.NoteDoesNotExist, displayIndex, string.Join(", ", available)));
}
try
@@ -1408,7 +1390,7 @@ private List PinNote(string indexStr, bool pin)
var idPrefix = $"[id:{noteToUpdate.Id}]";
var index = rawLines.FindIndex(line => line.StartsWith(idPrefix));
if (index < 0)
- return ErrorResult("Note not found", "Could not find the note in the file.");
+ return ErrorResult(Resources.NoteNotFound, Resources.CouldNotFindNoteInFile);
var entryToChange = NoteEntry.Parse(rawLines[index]);
entryToChange.IsPinned = pin;
@@ -1416,11 +1398,11 @@ private List PinNote(string indexStr, bool pin)
WriteNotes(rawLines);
_lastDeletedNoteRaw = null;
- return SingleInfoResult($"Note {(pin ? "pinned" : "unpinned")}", $"[{displayIndex}] {entryToChange.Text}", true);
+ return SingleInfoResult(pin ? Resources.NotePinned : Resources.NoteUnpinned, $"[{displayIndex}] {entryToChange.Text}", true);
}
catch (Exception ex)
{
- return ErrorResult($"Error {(pin ? "pinning" : "unpinning")} note", ex.Message);
+ return ErrorResult(pin ? Resources.ErrorPinningNote : Resources.ErrorUnpinningNote, ex.Message);
}
}
@@ -1428,7 +1410,7 @@ private List SortNotes(string args)
{
var notes = ReadNotes();
if (!notes.Any())
- return SingleInfoResult("No notes to sort", "");
+ return SingleInfoResult(Resources.NoNotesToSort, "");
var sortType = args.ToLowerInvariant().Trim();
var descending = sortType.EndsWith(" desc");
@@ -1455,7 +1437,7 @@ private List SortNotes(string args)
.ThenBy(n => n.DisplayIndex);
break;
default:
- return SingleInfoResult("Invalid sort type", "Use 'qq sort date [asc|desc]' or 'qq sort alpha [asc|desc]'");
+ return SingleInfoResult(Resources.InvalidSortType, Resources.InvalidSortTypeDesc);
}
try
@@ -1463,11 +1445,11 @@ private List SortNotes(string args)
var linesToWrite = sorted.Select(n => n.ToFileLine()).ToList();
WriteNotes(linesToWrite);
_lastDeletedNoteRaw = null;
- return SingleInfoResult("Notes sorted", $"Sorted by {sortType} {(descending ? "descending" : (ascending ? "ascending" : "(default asc)"))}", true);
+ return SingleInfoResult(Resources.NotesSorted, string.Format(Resources.NotesSortedDesc, sortType, descending ? Resources.Descending : (ascending ? Resources.Ascending : Resources.DefaultAsc)), true);
}
catch (Exception ex)
{
- return ErrorResult("Error sorting notes", ex.Message);
+ return ErrorResult(Resources.ErrorSortingNotes, ex.Message);
}
}
@@ -1481,20 +1463,20 @@ private List EditNote(string noteNumberStr)
if (noteToEdit == null)
{
var available = notes.Select(n => n.DisplayIndex).OrderBy(x => x).ToList();
- return SingleInfoResult("Note not found", $"Note #{displayIndex} does not exist. Available: {string.Join(", ", available)}");
+ return SingleInfoResult(Resources.NoteNotFound, string.Format(Resources.NoteDoesNotExist, displayIndex, string.Join(", ", available)));
}
return new List
{
new Result
{
- Title = $"Edit note #{displayIndex}",
- SubTitle = $"Press Enter to edit: {Truncate(StripTimestamp(noteToEdit.Text), 60)}",
+ Title = string.Format(Resources.EditNoteNumber, displayIndex),
+ SubTitle = string.Format(Resources.EditNoteSubtitle, Truncate(StripTimestamp(noteToEdit.Text), 60)),
IcoPath = IconPath,
Action = _ =>
{
var textToEdit = StripTimestamp(noteToEdit.Text);
- var newText = Interaction.InputBox($"Edit note #{displayIndex}", "Edit QuickNote", textToEdit);
+ var newText = Interaction.InputBox(string.Format(Resources.EditNoteNumber, displayIndex), Resources.EditQuickNote, textToEdit);
if (string.IsNullOrEmpty(newText) || newText == textToEdit)
{
return true;
@@ -1515,7 +1497,7 @@ private List EditNote(string noteNumberStr)
var index = rawLines.FindIndex(line => line.StartsWith(idPrefix));
if (index < 0)
{
- Context?.API.ShowMsg("Error", "Could not find the note in the file.", IconPath);
+ Context?.API.ShowMsg(Resources.Error, Resources.CouldNotFindNoteInFile, IconPath);
return true;
}
@@ -1526,7 +1508,7 @@ private List EditNote(string noteNumberStr)
}
catch (Exception ex)
{
- Context?.API.ShowMsg("Error saving edited note", ex.Message, IconPath);
+ Context?.API.ShowMsg(Resources.ErrorSavingEditedNote, ex.Message, IconPath);
return true;
}
}
@@ -1537,7 +1519,7 @@ private List EditNote(string noteNumberStr)
private void EditNoteInline(NoteEntry note)
{
var textToEdit = StripTimestamp(note.Text);
- var newText = Interaction.InputBox($"Edit note #{note.DisplayIndex}", "Edit QuickNote", textToEdit);
+ var newText = Interaction.InputBox(string.Format(Resources.EditNoteNumber, note.DisplayIndex), Resources.EditQuickNote, textToEdit);
if (string.IsNullOrEmpty(newText) || newText == textToEdit)
{
return;
@@ -1556,7 +1538,7 @@ private void EditNoteInline(NoteEntry note)
var index = rawLines.FindIndex(line => line.StartsWith(idPrefix));
if (index < 0)
{
- Context?.API.ShowMsg("Error", "Could not find note to save edit. Refresh and retry.", IconPath);
+ Context?.API.ShowMsg(Resources.Error, Resources.CouldNotFindNoteToSaveEdit, IconPath);
return;
}
@@ -1567,7 +1549,7 @@ private void EditNoteInline(NoteEntry note)
}
catch (Exception ex)
{
- Context?.API.ShowMsg("Error saving note", ex.Message, IconPath);
+ Context?.API.ShowMsg(Resources.ErrorSavingNote, ex.Message, IconPath);
}
}
@@ -1581,10 +1563,10 @@ private List ViewNote(string noteNumberStr)
if (note == null)
{
var available = notes.Select(n => n.DisplayIndex).OrderBy(x => x).ToList();
- return SingleInfoResult("Note not found", $"Note #{displayIndex} does not exist. Available: {string.Join(", ", available)}");
+ return SingleInfoResult(Resources.NoteNotFound, string.Format(Resources.NoteDoesNotExist, displayIndex, string.Join(", ", available)));
}
- return new List { CreateNoteResult(note, "Press Enter to copy without timestamp | Ctrl+C for full note | Right-click for options") };
+ return new List { CreateNoteResult(note, Resources.ViewNoteSubtitle) };
}
private List BackupNotes()
@@ -1592,7 +1574,7 @@ private List BackupNotes()
try
{
if (!File.Exists(_notesPath))
- return SingleInfoResult("No notes file to backup", "The notes file doesn't exist.");
+ return SingleInfoResult(Resources.NoNotesFileToBackup, Resources.NotesFileDoesntExist);
var notesDir = Path.GetDirectoryName(_notesPath)!;
var backupFile = Path.Combine(notesDir, $"notes_backup_{DateTime.Now:yyyyMMdd_HHmmss}.txt");
@@ -1607,12 +1589,12 @@ private List BackupNotes()
Log.Exception("Failed to open backup folder", ex, GetType());
}
- return SingleInfoResult("Backup created",
- $"Backup saved to: {backupFile}\nBackup folder opened.", true);
+ return SingleInfoResult(Resources.BackupCreated,
+ string.Format(Resources.BackupCreatedDesc, backupFile), true);
}
catch (Exception ex)
{
- return ErrorResult("Error creating backup", ex.Message);
+ return ErrorResult(Resources.ErrorCreatingBackup, ex.Message);
}
}
@@ -1621,12 +1603,12 @@ private List ExportNotes()
try
{
if (!File.Exists(_notesPath))
- return SingleInfoResult("No notes file to export", "The notes file doesn't exist.");
+ return SingleInfoResult(Resources.NoNotesFileToExport, Resources.NotesFileDoesntExist);
var notes = ReadNotes();
if (!notes.Any())
{
- return SingleInfoResult("No notes to export", "Your notes file is empty.");
+ return SingleInfoResult(Resources.NoNotesToExport, Resources.NotesFileEmpty);
}
var notesDir = Path.GetDirectoryName(_notesPath)!;
@@ -1655,12 +1637,12 @@ private List ExportNotes()
Log.Exception("Failed to open export folder", ex, GetType());
}
- return SingleInfoResult("Export created",
- $"Export saved to: {exportFile}\nExport folder opened.", true);
+ return SingleInfoResult(Resources.ExportCreated,
+ string.Format(Resources.ExportCreatedDesc, exportFile), true);
}
catch (Exception ex)
{
- return ErrorResult("Error creating export", ex.Message);
+ return ErrorResult(Resources.ErrorCreatingExport, ex.Message);
}
}
@@ -1671,19 +1653,10 @@ private List HelpCommand()
private Result HelpResult()
{
- var helpText =
- "qq Add note ✏️ | qq pin Pin note 📌\n" +
- "qq search Search notes 🔍 | qq searchtag Search by tag\n" +
- "qq view View note | qq sort Sort notes\n" +
- "qq edit Edit note ✏️ | qq pin/unpin Pin/unpin note 📌\n" +
- "qq del Delete note 🗑️ | qq backup/export Backup/Export notes 💾\n" +
- "qq delall Delete all notes | qq undo Undo last delete\n" +
- "qq tagstyle