-
-
Notifications
You must be signed in to change notification settings - Fork 448
IPluginHotkey Interface for Global Hotkey & Window Hotkey / Support Rename File & Folder by Hotkey or Context Menu #3770
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
IPluginHotkey Interface for Global Hotkey & Window Hotkey / Support Rename File & Folder by Hotkey or Context Menu #3770
Conversation
🥷 Code experts: Jack251970, onesounds Jack251970, onesounds have most 👩💻 activity in the files. See details
Activity based on git-commit:
Knowledge based on git-blame:
Activity based on git-commit:
Knowledge based on git-blame:
Activity based on git-commit:
Knowledge based on git-blame:
Activity based on git-commit:
Knowledge based on git-blame:
Activity based on git-commit:
Knowledge based on git-blame:
Activity based on git-commit:
Knowledge based on git-blame:
Activity based on git-commit:
Knowledge based on git-blame:
Activity based on git-commit:
Knowledge based on git-blame:
Activity based on git-commit:
Knowledge based on git-blame:
Activity based on git-commit:
Knowledge based on git-blame:
Activity based on git-commit:
Knowledge based on git-blame: To learn more about /:\ gitStream - Visit our Docs |
Refactored hotkey management to improve thread safety by replacing `Dictionary` with `ConcurrentDictionary` and introducing a lock for updates. Added new methods for hotkey initialization, validation, and translation updates. Enhanced plugin initialization by integrating hotkey checks and removing redundant calls. Refactored action keyword management with new methods for adding and removing keywords, ensuring thread safety with `TryAdd`, `AddOrUpdate`, and `TryRemove`. Introduced the `PluginHotkeyChangedEvent` class to encapsulate hotkey change events. Improved thread safety and performance by using thread-safe collections and LINQ. Cleaned up code by removing deprecated methods, redundant comments, and outdated TODOs. Fixed concurrency issues and ensured consistent updates to plugin metadata and hotkey information.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 39 out of 40 changed files in this pull request and generated 5 comments.
Comments suppressed due to low confidence (1)
Flow.Launcher/Helper/HotKeyMapper.cs:1
- [nitpick] Using discard variable
_
as an out parameter name can be confusing. Consider usingout _
discard pattern instead:_windowPluginHotkeys.TryRemove(oldHotkey, out _);
using System;
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Flow.Launcher.Core/Plugin/PluginManager.cs (1)
353-372
: Add plugins implementing IPluginHotkey to the _hotkeyPlugins collection.
GetHotkeyPlugins()
(lines 615-618) queries_hotkeyPlugins
, but no code adds plugins to this collection inAddPluginToLists
. Plugins implementingIPluginHotkey
should be added to_hotkeyPlugins
here, similar to how other feature-interface plugins are tracked.Apply this diff:
private static void AddPluginToLists(PluginPair pair) { if (pair.Plugin is IContextMenu) { _contextMenuPlugins.Add(pair); } if (pair.Plugin is IAsyncHomeQuery) { _homePlugins.Add(pair); } if (pair.Plugin is IPluginI18n) { _translationPlugins.Add(pair); } + if (pair.Plugin is IPluginHotkey) + { + _hotkeyPlugins.Add(pair); + } if (pair.Plugin is IAsyncExternalPreview) { _externalPreviewPlugins.Add(pair); } _allInitializedPlugins.TryAdd(pair.Metadata.ID, pair); }
♻️ Duplicate comments (2)
Flow.Launcher/Helper/HotKeyMapper.cs (1)
46-152
: Clear RegisteredHotkeys before repopulating to prevent duplicates.
InitializeRegisteredHotkeys
adds items to_settings.RegisteredHotkeys
(lines 142, 243, 274, 311, 317) without first clearing the collection. IfInitialize()
is called multiple times (e.g., on settings reload or plugin changes), stale and duplicate entries will accumulate.Add a clear operation at the beginning of
InitializeRegisteredHotkeys
:private static void InitializeRegisteredHotkeys() { + // Clear existing hotkeys to avoid duplicates on re-initialization + _settings.RegisteredHotkeys.Clear(); + // Fixed hotkeys & Editable hotkeys var list = new List<RegisteredHotkeyData> {Based on past review comments.
Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml (1)
146-150
: Resolve default hotkey conflict between OpenContextMenuHotkey and SettingWindowHotkey.Both this
OpenContextMenuHotkey
card (line 147) and theSettingWindowHotkey
card (line 167) specifyDefaultHotkey="Ctrl+I"
, creating a conflict. Users cannot have both hotkeys enabled with their default values simultaneously.Apply this diff to resolve the conflict by using an empty default for this card:
<flowlauncher:HotkeyControl - DefaultHotkey="Ctrl+I" + DefaultHotkey="" Type="OpenContextMenuHotkey" ValidateKeyGesture="False" />
🧹 Nitpick comments (1)
Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs (1)
87-95
: Consider removing unnecessary focus change before closing.Line 91 focuses the done button immediately before calling
OnDoneButtonClick
, which then closes the window at line 79. Since the window closes immediately, the focus change has no visible effect and adds unnecessary UI work.Apply this diff to streamline the logic:
private void RenameTb_OnKeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) { - btnDone.Focus(); OnDoneButtonClick(sender, e); e.Handled = true; } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
Flow.Launcher.Core/Plugin/PluginManager.cs
(6 hunks)Flow.Launcher.Core/Resource/Internationalization.cs
(2 hunks)Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs
(2 hunks)Flow.Launcher.Infrastructure/UserSettings/Settings.cs
(4 hunks)Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs
(1 hunks)Flow.Launcher/Helper/HotKeyMapper.cs
(4 hunks)Flow.Launcher/Languages/en.xaml
(1 hunks)Flow.Launcher/MainWindow.xaml
(0 hunks)Flow.Launcher/Resources/Pages/WelcomePage2.xaml
(0 hunks)Flow.Launcher/Resources/Pages/WelcomePage3.xaml
(4 hunks)Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs
(0 hunks)Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs
(0 hunks)Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml
(5 hunks)Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs
(2 hunks)Flow.Launcher/ViewModel/MainViewModel.cs
(2 hunks)Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs
(1 hunks)
💤 Files with no reviewable changes (4)
- Flow.Launcher/Resources/Pages/WelcomePage2.xaml
- Flow.Launcher/MainWindow.xaml
- Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs
- Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs
🚧 Files skipped from review as they are similar to previous changes (3)
- Flow.Launcher/Languages/en.xaml
- Flow.Launcher.Core/Resource/Internationalization.cs
- Flow.Launcher/Resources/Pages/WelcomePage3.xaml
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025年09月28日T03:59:59.693Z
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3770
File: Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs:75-80
Timestamp: 2025年09月28日T03:59:59.693Z
Learning: In Flow.Launcher's Explorer plugin rename dialog (RenameFile.xaml.cs), the dialog should close unconditionally after calling RenameThing.Rename(), even on validation failures, because RenameThing.Rename() displays error messages via popup notifications. This is the intended UX design.
Applied to files:
Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs
📚 Learning: 2025年09月05日T11:56:27.267Z
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3593
File: Flow.Launcher/HotkeyControlDialog.xaml:6-6
Timestamp: 2025年09月05日T11:56:27.267Z
Learning: In Flow.Launcher's migration to iNKORE.UI.WPF.Modern UI framework, dialog resource keys like PopuBGColor, PopupButtonAreaBGColor, PopupButtonAreaBorderColor, and PopupTextColor are provided by the iNKORE.UI.WPF.Modern framework itself, not defined locally in the codebase theme files.
Applied to files:
Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs
📚 Learning: 2025年09月28日T03:57:43.995Z
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3770
File: Flow.Launcher/Helper/HotKeyMapper.cs:0-0
Timestamp: 2025年09月28日T03:57:43.995Z
Learning: In Flow.Launcher/Helper/HotKeyMapper.cs, the resource descriptions for arrow keys are intentionally swapped - Up/Down keys use "HotkeyLeftRightDesc" and Left/Right keys use "HotkeyUpDownDesc". This is documented with an inline comment and is intended behavior, not a bug.
Applied to files:
Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml
Flow.Launcher/Helper/HotKeyMapper.cs
📚 Learning: 2025年03月28日T21:12:13.386Z
Learnt from: onesounds
PR: Flow-Launcher/Flow.Launcher#3394
File: Flow.Launcher/Themes/Darker Glass.xaml:134-141
Timestamp: 2025年03月28日T21:12:13.386Z
Learning: In Flow.Launcher, hotkey styling is implemented with a two-component structure: a Border element with style `ItemHotkeyBGStyle` that provides background and border styling, containing a TextBlock with style `ItemHotkeyStyle` that handles the text styling.
Applied to files:
Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml
📚 Learning: 2025年06月24日T19:06:48.344Z
Learnt from: Koisu-unavailable
PR: Flow-Launcher/Flow.Launcher#3770
File: Flow.Launcher/ViewModel/MainViewModel.cs:0-0
Timestamp: 2025年06月24日T19:06:48.344Z
Learning: In Flow.Launcher's Explorer plugin results, the SubTitle property always contains the directory containing the file. For file results, Title contains the filename and SubTitle contains the parent directory. For directory results, SubTitle contains the directory path itself.
Applied to files:
Flow.Launcher/ViewModel/MainViewModel.cs
🧬 Code graph analysis (6)
Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs (1)
Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs (3)
RenameThing
(7-109)Rename
(9-54)Rename
(62-108)
Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs (4)
Flow.Launcher.Core/Plugin/PluginManager.cs (16)
PluginManager
(26-1225)PluginManager
(177-183)ChangePluginHotkey
(904-917)ChangePluginHotkey
(919-955)Task
(92-110)Task
(112-120)Task
(126-133)Task
(135-142)Task
(144-151)Task
(264-333)Task
(419-483)Task
(485-535)Task
(537-571)Task
(1019-1036)Task
(1043-1046)Task
(1133-1220)Flow.Launcher.Infrastructure/Image/ImageLoader.cs (1)
ImageLoader
(17-411)Flow.Launcher/HotkeyControl.xaml.cs (3)
HotkeyControl
(14-344)HotkeyControl
(235-243)Task
(265-285)Flow.Launcher/App.xaml.cs (2)
App
(32-471)App
(58-122)
Flow.Launcher.Core/Plugin/PluginManager.cs (4)
Flow.Launcher.Plugin/PluginHotkey.cs (7)
BasePluginHotkey
(11-61)BasePluginHotkey
(17-20)SearchWindowPluginHotkey
(84-97)SearchWindowPluginHotkey
(89-91)GlobalPluginHotkey
(66-79)GlobalPluginHotkey
(71-73)PluginHotkey
(118-134)Flow.Launcher.Plugin/PluginMetadata.cs (1)
PluginMetadata
(10-168)Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs (1)
ChangePluginHotkey
(109-119)Flow.Launcher/Helper/HotKeyMapper.cs (1)
GlobalPluginHotkey
(664-677)
Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs (1)
Flow.Launcher/ViewModel/MainViewModel.cs (11)
RelayCommand
(339-347)RelayCommand
(349-361)RelayCommand
(363-371)RelayCommand
(380-392)RelayCommand
(394-406)RelayCommand
(408-440)RelayCommand
(442-453)RelayCommand
(455-487)RelayCommand
(489-542)RelayCommand
(576-581)RelayCommand
(583-587)
Flow.Launcher.Infrastructure/UserSettings/Settings.cs (3)
Flow.Launcher.Infrastructure/UserSettings/CustomShortcutModel.cs (1)
CustomShortcutModel
(69-79)Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs (1)
PluginsSettings
(7-214)Flow.Launcher/Helper/HotKeyMapper.cs (6)
RegisteredHotkeyData
(328-331)RegisteredHotkeyData
(333-338)RegisteredHotkeyData
(355-360)RegisteredHotkeyData
(362-373)RegisteredHotkeyData
(375-382)RegisteredHotkeyData
(635-640)
Flow.Launcher/ViewModel/MainViewModel.cs (1)
Flow.Launcher/ViewModel/ResultsViewModel.cs (3)
ResultsViewModel
(16-379)ResultsViewModel
(29-33)ResultsViewModel
(35-52)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: gitStream.cm
- GitHub Check: gitStream.cm
- GitHub Check: Report (PR)
- GitHub Check: gitStream.cm
- GitHub Check: build
🔇 Additional comments (9)
Flow.Launcher/ViewModel/MainViewModel.cs (2)
576-581
: LGTM! Defensive hotkey handling.The new command properly gates launcher toggling behind the
ShouldIgnoreHotkeys()
check, preventing unwanted toggling during fullscreen apps or game mode. This aligns well with the PR's hotkey system improvements.
1858-1861
: LGTM! Appropriate internal accessor.The method provides controlled read-only access to the private
SelectedResults
property for other classes within the assembly. This is a clean way to expose internal state without making the property itself public.Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs (1)
469-487
: Command-based hook looks goodExposing the dialog jump trigger as an
IRelayCommand
aligns it with the new hotkey mapper and the guard againstEnableDialogJump
keeps the handler inert when disabled. Nice tidy refactor.Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs (3)
30-47
: LGTM! Constructor properly initializes the rename dialog.The constructor correctly:
- Stores the FileSystemInfo and original path for rename operations
- Initializes NewFileName with the current name
- Hides the taskbar icon for a cleaner modal experience
- Sets focus to the textbox for immediate editing
- Implements the requested ESC-to-close behavior
52-72
: Text selection logic correctly handles edge cases.The implementation properly addresses the previous review concern about
IndexOf
returning -1 by:
- Using
LastIndexOf
to find the base name position- Falling back to
SelectAll
when the base name isn't found (line 65-69)- Consistently dispatching all UI updates via
textBox.Dispatcher
- Selecting only the filename portion (excluding extension) for files while selecting all text for directories
Based on learnings
75-80
: Unconditional close is correct per design.The dialog closes immediately after calling
RenameThing.Rename()
regardless of success or failure becauseRenameThing.Rename()
displays error messages via popup notifications. This provides consistent UX where users receive clear error feedback without manually closing a lingering dialog.Based on learnings
Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs (3)
378-389
: LGTM! File name validation is comprehensive.The method correctly validates file names by:
- Checking for Windows reserved device names first
- Rejecting names containing invalid filename characters
- Rejecting null or whitespace-only names
391-404
: LGTM! Directory name validation addresses previous concern.The null/whitespace check at line 396 correctly addresses the previous review comment about rejecting blank directory names. The method now properly:
- Rejects null or whitespace-only names early
- Checks for reserved device names
- Validates against invalid path characters including explicit '/' and '\' separators
406-416
: LGTM! Reserved name detection is correctly implemented.The implementation properly:
- Includes all Windows reserved device names (COM1-COM9, LPT1-LPT9, CON, PRN, AUX, NUL)
- Strips the file extension before checking (handles cases like "CON.txt")
- Uses case-insensitive comparison via
ToUpperInvariant()
Note:
GetFileNameWithoutExtension
removes only the last extension, so "CON.txt.log" would have base name "CON.txt" and not be flagged as reserved, which is correct Windows behavior.
Previously, `UpdatePluginHotkeyInfoTranslations` was called for all plugins, even those without hotkey support. This commit adds a conditional check to ensure the method is only invoked for plugins implementing the `IPluginHotkey` interface. Additionally, comments were added to clarify the purpose of the hotkey update logic. These changes improve robustness by preventing unnecessary or invalid calls for non-hotkey plugins.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (1)
Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs (1)
408-416
: Consider simplifying the return logic.The if-else structure can be replaced with a direct return, and adding a defensive null check improves robustness even though callers should validate first.
Apply this diff to simplify:
private static bool IsReservedName(string name) { + if (string.IsNullOrEmpty(name)) return false; var nameWithoutExtension = Path.GetFileNameWithoutExtension(name).ToUpperInvariant(); - if (ReservedNames.Contains(nameWithoutExtension)) - { - return true; - } - return false; + return ReservedNames.Contains(nameWithoutExtension); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
Flow.Launcher.Plugin/PluginHotkey.cs
(1 hunks)Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs
(1 hunks)Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs
🧰 Additional context used
🧬 Code graph analysis (1)
Flow.Launcher.Plugin/PluginHotkey.cs (2)
Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs (3)
Plugin
(200-207)Plugin
(209-213)Plugin
(216-244)Flow.Launcher/Helper/HotKeyMapper.cs (1)
GlobalPluginHotkey
(664-677)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: gitStream.cm
- GitHub Check: gitStream.cm
- GitHub Check: gitStream.cm
🔇 Additional comments (2)
Flow.Launcher.Plugin/PluginHotkey.cs (1)
1-134
: Well-designed hotkey model hierarchy.The class structure follows solid design principles:
- Protected constructor pattern on
BasePluginHotkey
correctly prevents direct instantiation while allowing inheritance.- Clear separation between runtime models (with behavior via
Action
properties) and storage DTOs (PluginHotkey
).- Immutable
HotkeyType
property ensures type safety after construction.- Comprehensive XML documentation aids IntelliSense and API discoverability.
The nullable
Action
properties are safely handled with null-conditional operators inHotKeyMapper.cs
(line 676:pluginHotkey.Action?.Invoke()
).Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs (1)
394-404
: LGTM!The validation logic is correct and properly ordered—null/whitespace check prevents exceptions, reserved name check catches Windows device names, and the invalid character check includes path separators. The array optimization from the previous review has been successfully applied.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs (1)
472-487
: Guard against uninitialized/disabled state and null _lastExplorer to avoid runtime exceptionsOnToggleHotkey only checks settings. If DialogJump isn’t initialized/enabled or no explorer has been detected yet, NavigateDialogPathAsync will index _dialogJumpExplorers with a null key (see Line 821-824), throwing and logging an exception. Add guards and ensure an explorer is available before navigating.
Apply this diff:
private static void OnToggleHotkey() { - if (!_settings.EnableDialogJump) return; - - _ = Task.Run(async () => - { - try - { - await NavigateDialogPathAsync(PInvoke.GetForegroundWindow()); - } - catch (System.Exception ex) - { - Log.Exception(ClassName, "Failed to navigate dialog path", ex); - } - }); + // Feature toggle + runtime readiness + if (!_settings.EnableDialogJump || !_initialized || !_enabled) return; + + // Ensure we have a valid last explorer (avoid null index on _dialogJumpExplorers[_lastExplorer]) + var hasExplorer = false; + lock (_lastExplorerLock) + { + hasExplorer = _lastExplorer is not null; + } + if (!hasExplorer) + { + try { hasExplorer = RefreshLastExplorer(); } + catch { hasExplorer = false; } + } + if (!hasExplorer) return; + + var hwnd = PInvoke.GetForegroundWindow(); + _ = Task.Run(async () => + { + try + { + await NavigateDialogPathAsync(hwnd); + } + catch (System.Exception ex) + { + Log.Exception(ClassName, "Failed to navigate dialog path", ex); + } + }); }
♻️ Duplicate comments (3)
Flow.Launcher.Core/Plugin/PluginManager.cs (2)
615-618
: Returns empty list due to unpopulated_hotkeyPlugins
.This method is correctly implemented but will always return an empty list because
_hotkeyPlugins
is never populated. Once the critical issue (adding plugins to_hotkeyPlugins
inCheckPluginHotkeys
) is resolved, this method will function as intended.
919-955
: Remove unusedKeyGestureConverter
variable.Line 924 declares a
KeyGestureConverter
instance that is never used. This was noted in a previous review.Apply this diff to remove the unused variable:
public static void ChangePluginHotkey(PluginMetadata plugin, SearchWindowPluginHotkey pluginHotkey, HotkeyModel newHotkey) { var oldHotkeyItem = plugin.PluginHotkeys.First(h => h.Id == pluginHotkey.Id); var settingHotkeyItem = Settings.GetPluginSettings(plugin.ID).pluginHotkeys.First(h => h.Id == pluginHotkey.Id); var oldHotkeyStr = settingHotkeyItem.Hotkey; - var converter = new KeyGestureConverter(); var oldHotkey = new HotkeyModel(oldHotkeyStr); var newHotkeyStr = newHotkey.ToString(); // Update hotkey in plugin metadata & setting oldHotkeyItem.Hotkey = newHotkeyStr; settingHotkeyItem.Hotkey = newHotkeyStr; // Update window plugin hotkey dictionary var oldHotkeyModels = _windowPluginHotkeys[oldHotkey]; _windowPluginHotkeys[oldHotkey] = oldHotkeyModels.Where(x => x.Item1.ID != plugin.ID || x.Item2.Id != pluginHotkey.Id).ToList(); if (_windowPluginHotkeys[oldHotkey].Count == 0) { _windowPluginHotkeys.TryRemove(oldHotkey, out var _); } if (_windowPluginHotkeys.TryGetValue(newHotkey, out var newHotkeyModels)) { var newList = newHotkeyModels.ToList(); newList.Add((plugin, pluginHotkey)); _windowPluginHotkeys[newHotkey] = newList; } else { _windowPluginHotkeys[newHotkey] = [ (plugin, pluginHotkey) ]; } PluginHotkeyChanged?.Invoke(new PluginHotkeyChangedEvent(oldHotkey, newHotkey, plugin, pluginHotkey)); }
Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml (1)
146-149
: Fix default for "Open context menu" to avoid conflict with Settings (Ctrl+I)This sets DefaultHotkey to Ctrl+I, conflicting with SettingWindowHotkey (also Ctrl+I) and diverges from Settings default ("Ctrl+O"). Align to empty or "Ctrl+O".
Apply:
- <flowlauncher:HotkeyControl - DefaultHotkey="Ctrl+I" + <flowlauncher:HotkeyControl + DefaultHotkey="" Type="OpenContextMenuHotkey" ValidateKeyGesture="False" />
🧹 Nitpick comments (4)
Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs (1)
469-471
: Prefer AsyncRelayCommand and a thread-safe lazy initAvoid manual Task.Run inside a RelayCommand; AsyncRelayCommand expresses intent, handles state, and simplifies testing. Also consider Lazy (or volatile) for thread-safe initialization.
Example:
- private static RelayCommand _dialogJumpCommand; - public static IRelayCommand DialogJumpCommand => _dialogJumpCommand ??= new RelayCommand(OnToggleHotkey); + private static CommunityToolkit.Mvvm.Input.IAsyncRelayCommand _dialogJumpCommand; + public static IAsyncRelayCommand DialogJumpCommand => + _dialogJumpCommand ??= new AsyncRelayCommand(ExecuteDialogJumpAsync, CanExecuteDialogJump); + + private static bool CanExecuteDialogJump() => + _settings.EnableDialogJump && _initialized && _enabled; + + private static async Task ExecuteDialogJumpAsync() + { + if (!CanExecuteDialogJump()) return; + var hasExplorer = false; + lock (_lastExplorerLock) { hasExplorer = _lastExplorer is not null; } + if (!hasExplorer) + { + try { hasExplorer = RefreshLastExplorer(); } catch { hasExplorer = false; } + } + if (!hasExplorer) return; + var hwnd = PInvoke.GetForegroundWindow(); + try { await NavigateDialogPathAsync(hwnd); } + catch (Exception ex) { Log.Exception(ClassName, "Failed to navigate dialog path", ex); } + }Flow.Launcher.Core/Plugin/PluginManager.cs (1)
834-856
: Consider error handling for invalid hotkey strings.If a plugin provides an invalid or malformed hotkey string (empty, whitespace, or unparseable), the
HotkeyModel
constructor (line 846) might throw an exception, causingCheckPluginHotkeys
to fail and potentially preventing the plugin from initializing properly.Wrap the
HotkeyModel
construction in a try-catch and skip invalid hotkeys:private static void InitializeWindowPluginHotkey(PluginPair pair) { foreach (var info in GetPluginHotkeyInfo(pair.Metadata.ID)) { var pluginPair = info.Key; var hotkeyInfo = info.Value; var metadata = pluginPair.Metadata; foreach (var hotkey in hotkeyInfo) { if (hotkey.HotkeyType == HotkeyType.SearchWindow && hotkey is SearchWindowPluginHotkey searchWindowHotkey) { var hotkeySetting = metadata.PluginHotkeys.Find(h => h.Id == hotkey.Id)?.Hotkey ?? hotkey.DefaultHotkey; - var hotkeyModel = new HotkeyModel(hotkeySetting); + HotkeyModel hotkeyModel; + try + { + hotkeyModel = new HotkeyModel(hotkeySetting); + } + catch (Exception e) + { + PublicApi.Instance.LogWarn(ClassName, $"Invalid hotkey '{hotkeySetting}' for plugin {metadata.Name}, skipping: {e.Message}"); + continue; + } if (!_windowPluginHotkeys.TryGetValue(hotkeyModel, out var list)) { list = []; _windowPluginHotkeys[hotkeyModel] = list; } list.Add((pluginPair.Metadata, searchWindowHotkey)); } } } }Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs (2)
44-107
: Prevent duplicate plugin sections on repeated loadsLoaded can fire multiple times; either clear existing items or guard.
Apply:
private void PluginHotkeySettings_Loaded(object sender, RoutedEventArgs e) { + if (PluginHotkeySettings.Children.Count > 0) + return; // already built
71-76
: Guard against null Glyph to avoid NREFallback to a default glyph when plugin hotkey doesn’t supply one.
Apply:
- var card = new SettingsCard() - { - Header = hotkey.Name, - Description = hotkey.Description, - HeaderIcon = new FontIcon() { Glyph = hotkey.Glyph.Glyph } - }; + var glyph = hotkey.Glyph?.Glyph ?? "\uE8A1"; // default icon + var card = new SettingsCard() + { + Header = hotkey.Name, + Description = hotkey.Description, + HeaderIcon = new FontIcon() { Glyph = glyph } + };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
Flow.Launcher.Core/Plugin/PluginManager.cs
(6 hunks)Flow.Launcher.Core/Resource/Internationalization.cs
(2 hunks)Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs
(2 hunks)Flow.Launcher.Infrastructure/UserSettings/Settings.cs
(4 hunks)Flow.Launcher.Plugin/PluginHotkey.cs
(1 hunks)Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs
(1 hunks)Flow.Launcher/Helper/HotKeyMapper.cs
(4 hunks)Flow.Launcher/Languages/en.xaml
(1 hunks)Flow.Launcher/MainWindow.xaml
(0 hunks)Flow.Launcher/Resources/Pages/WelcomePage2.xaml
(0 hunks)Flow.Launcher/Resources/Pages/WelcomePage3.xaml
(4 hunks)Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs
(0 hunks)Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs
(0 hunks)Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml
(5 hunks)Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs
(2 hunks)Flow.Launcher/ViewModel/MainViewModel.cs
(2 hunks)Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs
(1 hunks)
💤 Files with no reviewable changes (4)
- Flow.Launcher/MainWindow.xaml
- Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs
- Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs
- Flow.Launcher/Resources/Pages/WelcomePage2.xaml
🚧 Files skipped from review as they are similar to previous changes (3)
- Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs
- Flow.Launcher/Languages/en.xaml
- Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025年09月05日T11:56:27.267Z
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3593
File: Flow.Launcher/HotkeyControlDialog.xaml:6-6
Timestamp: 2025年09月05日T11:56:27.267Z
Learning: In Flow.Launcher's migration to iNKORE.UI.WPF.Modern UI framework, dialog resource keys like PopuBGColor, PopupButtonAreaBGColor, PopupButtonAreaBorderColor, and PopupTextColor are provided by the iNKORE.UI.WPF.Modern framework itself, not defined locally in the codebase theme files.
Applied to files:
Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs
📚 Learning: 2025年06月24日T19:06:48.344Z
Learnt from: Koisu-unavailable
PR: Flow-Launcher/Flow.Launcher#3770
File: Flow.Launcher/ViewModel/MainViewModel.cs:0-0
Timestamp: 2025年06月24日T19:06:48.344Z
Learning: In Flow.Launcher's Explorer plugin results, the SubTitle property always contains the directory containing the file. For file results, Title contains the filename and SubTitle contains the parent directory. For directory results, SubTitle contains the directory path itself.
Applied to files:
Flow.Launcher/ViewModel/MainViewModel.cs
📚 Learning: 2025年09月28日T03:57:43.995Z
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3770
File: Flow.Launcher/Helper/HotKeyMapper.cs:0-0
Timestamp: 2025年09月28日T03:57:43.995Z
Learning: In Flow.Launcher/Helper/HotKeyMapper.cs, the resource descriptions for arrow keys are intentionally swapped - Up/Down keys use "HotkeyLeftRightDesc" and Left/Right keys use "HotkeyUpDownDesc". This is documented with an inline comment and is intended behavior, not a bug.
Applied to files:
Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml
Flow.Launcher/Helper/HotKeyMapper.cs
📚 Learning: 2025年03月28日T21:12:13.386Z
Learnt from: onesounds
PR: Flow-Launcher/Flow.Launcher#3394
File: Flow.Launcher/Themes/Darker Glass.xaml:134-141
Timestamp: 2025年03月28日T21:12:13.386Z
Learning: In Flow.Launcher, hotkey styling is implemented with a two-component structure: a Border element with style `ItemHotkeyBGStyle` that provides background and border styling, containing a TextBlock with style `ItemHotkeyStyle` that handles the text styling.
Applied to files:
Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml
🧬 Code graph analysis (8)
Flow.Launcher.Plugin/PluginHotkey.cs (2)
Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs (3)
Plugin
(200-207)Plugin
(209-213)Plugin
(216-244)Flow.Launcher/Helper/HotKeyMapper.cs (1)
GlobalPluginHotkey
(664-677)
Flow.Launcher.Core/Resource/Internationalization.cs (1)
Flow.Launcher.Core/Plugin/PluginManager.cs (3)
PluginManager
(26-1225)PluginManager
(177-183)UpdatePluginHotkeyInfoTranslations
(880-902)
Flow.Launcher.Core/Plugin/PluginManager.cs (2)
Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs (1)
ChangePluginHotkey
(109-119)Flow.Launcher/Helper/HotKeyMapper.cs (1)
GlobalPluginHotkey
(664-677)
Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs (2)
Flow.Launcher.Core/Plugin/PluginManager.cs (16)
PluginManager
(26-1225)PluginManager
(177-183)ChangePluginHotkey
(904-917)ChangePluginHotkey
(919-955)Task
(92-110)Task
(112-120)Task
(126-133)Task
(135-142)Task
(144-151)Task
(264-333)Task
(419-483)Task
(485-535)Task
(537-571)Task
(1019-1036)Task
(1043-1046)Task
(1133-1220)Flow.Launcher/HotkeyControl.xaml.cs (3)
HotkeyControl
(14-344)HotkeyControl
(235-243)Task
(265-285)
Flow.Launcher/ViewModel/MainViewModel.cs (1)
Flow.Launcher/ViewModel/ResultsViewModel.cs (3)
ResultsViewModel
(16-379)ResultsViewModel
(29-33)ResultsViewModel
(35-52)
Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs (1)
Flow.Launcher/ViewModel/MainViewModel.cs (11)
RelayCommand
(339-347)RelayCommand
(349-361)RelayCommand
(363-371)RelayCommand
(380-392)RelayCommand
(394-406)RelayCommand
(408-440)RelayCommand
(442-453)RelayCommand
(455-487)RelayCommand
(489-542)RelayCommand
(576-581)RelayCommand
(583-587)
Flow.Launcher.Infrastructure/UserSettings/Settings.cs (1)
Flow.Launcher/Helper/HotKeyMapper.cs (6)
RegisteredHotkeyData
(328-331)RegisteredHotkeyData
(333-338)RegisteredHotkeyData
(355-360)RegisteredHotkeyData
(362-373)RegisteredHotkeyData
(375-382)RegisteredHotkeyData
(635-640)
Flow.Launcher/Helper/HotKeyMapper.cs (7)
Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs (1)
DialogJump
(20-1107)Flow.Launcher/ViewModel/MainViewModel.cs (23)
MainViewModel
(32-2347)MainViewModel
(70-206)List
(1325-1385)ReQuery
(373-378)RelayCommand
(339-347)RelayCommand
(349-361)RelayCommand
(363-371)RelayCommand
(380-392)RelayCommand
(394-406)RelayCommand
(408-440)RelayCommand
(442-453)RelayCommand
(455-487)RelayCommand
(489-542)RelayCommand
(576-581)RelayCommand
(583-587)RelayCommand
(589-593)RelayCommand
(595-599)RelayCommand
(601-605)RelayCommand
(607-611)RelayCommand
(613-617)ShouldIgnoreHotkeys
(1882-1885)Result
(1761-1802)Result
(1804-1832)Flow.Launcher.Infrastructure/UserSettings/Settings.cs (1)
Settings
(16-763)Flow.Launcher.Core/Plugin/PluginManager.cs (14)
PluginManager
(26-1225)PluginManager
(177-183)List
(582-585)List
(587-598)List
(600-603)List
(610-613)List
(615-618)List
(656-683)PluginHotkeyChangedEvent
(959-969)ChangePluginHotkey
(904-917)ChangePluginHotkey
(919-955)Dictionary
(605-608)Dictionary
(858-872)Dictionary
(874-878)Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs (6)
RegisteredHotkeyData
(88-99)RegisteredHotkeyData
(130-141)RegisteredHotkeyData
(174-187)RegisteredHotkeyData
(221-234)SetHotkey
(240-243)ToString
(246-249)Flow.Launcher/MainWindow.xaml.cs (2)
MainWindow
(37-1499)MainWindow
(86-102)Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs (1)
KeyGesture
(129-132)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: build
- GitHub Check: gitStream.cm
- GitHub Check: gitStream.cm
🔇 Additional comments (13)
Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs (1)
2-2
: Imports LGTMRequired for ConcurrentDictionary and Relay/AsyncRelayCommand usage.
Also applies to: 9-9
Flow.Launcher.Core/Plugin/PluginManager.cs (6)
9-9
: LGTM: Import statements support hotkey functionality.The added namespaces provide the necessary types for the plugin hotkey subsystem.
Also applies to: 14-14
47-47
: LGTM: Appropriate use of Lock for thread safety.The
Lock
type (introduced in .NET 9) is correctly used to synchronize access toSettings.UpdatePluginHotkeyInfo()
during concurrent plugin initialization.
374-386
: LGTM: Hotkey initialization flow is well-structured.The method properly checks for
IPluginHotkey
implementation, initializes hotkey information, and uses a lock to safely update settings during concurrent plugin initialization. (Note: Ensure the missing_hotkeyPlugins.Add(pair)
call is added per the earlier comment.)
858-902
: LGTM: Hotkey query and translation methods are well-implemented.These methods provide clean APIs for:
- Retrieving plugin hotkey information (with optional filtering by plugin ID)
- Accessing window-specific hotkey mappings
- Updating hotkey metadata translations after language changes
The logic correctly handles edge cases and maintains consistency with the hotkey subsystem design.
904-917
: LGTM: Global hotkey change logic is correct.The method properly updates both the plugin metadata and persisted settings, then emits the
PluginHotkeyChanged
event to notify subscribers of the change. The use ofFirst()
is intentional to fail fast if the hotkey ID is not found (as confirmed by the maintainer in previous reviews).
959-969
: LGTM: Event class is well-designed.The
PluginHotkeyChangedEvent
class uses a primary constructor and exposes immutable properties for the old/new hotkey models, plugin metadata, and hotkey configuration. This design is clean and follows modern C# patterns.Flow.Launcher/ViewModel/MainViewModel.cs (2)
576-581
: Hotkey-aware toggle looks goodProperly guards ToggleFlowLauncher behind ShouldIgnoreHotkeys().
1858-1861
: Accessor for SelectedResults is fineInternal exposure is appropriate for in-assembly hotkey handling.
Flow.Launcher/Resources/Pages/WelcomePage3.xaml (1)
89-123
: Hotkey help updates read wellConsistent with new mappings (Enter, Shift+Enter, F5, Alt+Home/End, Ctrl+R/F12, etc.). No issues.
Also applies to: 128-221
Flow.Launcher.Plugin/PluginHotkey.cs (1)
11-61
: Plugin hotkey models are soundAPI surface is clear; HotkeyType set in ctor; nullable Action is guarded at call sites.
Also applies to: 63-79, 81-97, 99-114, 115-134
Flow.Launcher.Core/Resource/Internationalization.cs (1)
371-375
: Plugin hotkey translations wired correctlyUpdating names/descriptions via PluginManager ensures localized hotkey metadata reflects language changes.
Also applies to: 387-397
Flow.Launcher.Infrastructure/UserSettings/Settings.cs (1)
214-226
: Hotkey backing fields and live RegisteredHotkeys collection look goodProperty-changed semantics enable live rebinds; RegisteredHotkeys as ObservableCollection aligns with centralized mapper.
Ensure UI defaults match these values (e.g., OpenContextMenuHotkey = "Ctrl+O") to avoid confusion.
Also applies to: 228-240, 242-254, 671-677, 761-763
Enhanced the plugin management system by adding the `pair` object to the `_hotkeyPlugins` collection in `PluginManager.cs`. This ensures that hotkey-enabled plugins are properly tracked and managed within the `Flow.Launcher.Core.Plugin` namespace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
Flow.Launcher.Core/Plugin/PluginManager.cs (1)
925-925
: Remove unused variable.The
KeyGestureConverter
is declared but never used. This is dead code.Apply this diff:
var oldHotkeyItem = plugin.PluginHotkeys.First(h => h.Id == pluginHotkey.Id); var settingHotkeyItem = Settings.GetPluginSettings(plugin.ID).pluginHotkeys.First(h => h.Id == pluginHotkey.Id); var oldHotkeyStr = settingHotkeyItem.Hotkey; - var converter = new KeyGestureConverter(); var oldHotkey = new HotkeyModel(oldHotkeyStr); var newHotkeyStr = newHotkey.ToString();
🧹 Nitpick comments (1)
Flow.Launcher.Core/Plugin/PluginManager.cs (1)
920-956
: Consider atomicity of dictionary operations.The SearchWindowPluginHotkey change logic updates
_windowPluginHotkeys
through multiple non-atomic operations (lines 934-953). While this may be safe in practice (UI thread), concurrent hotkey changes could lead to race conditions.Consider wrapping the dictionary updates in a lock if concurrent changes become a concern:
lock (_pluginHotkeyInfoUpdateLock) { // Update window plugin hotkey dictionary var oldHotkeyModels = _windowPluginHotkeys[oldHotkey]; // ... rest of dictionary operations }Alternatively, if the operations are guaranteed to run on the UI thread, a comment documenting this assumption would be helpful.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
Flow.Launcher.Core/Plugin/PluginManager.cs
(6 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Report (PR)
- GitHub Check: build
- GitHub Check: gitStream.cm
- GitHub Check: gitStream.cm
🔇 Additional comments (13)
Flow.Launcher.Core/Plugin/PluginManager.cs (13)
9-9
: LGTM!The new imports are necessary for the hotkey infrastructure (HotkeyModel, event handling).
Also applies to: 14-14
36-36
: LGTM!The event declaration correctly uses the
event
keyword to prevent overwriting subscribers.
44-44
: LGTM!The
_hotkeyPlugins
field is now properly populated at line 385, addressing the past review concern.
47-49
: LGTM!Good use of modern C#
Lock
type andConcurrentDictionary
for thread-safe hotkey tracking.
314-316
: LGTM!Appropriate placement after plugin initialization and metadata translation updates.
374-387
: LGTM!The hotkey initialization flow is well-structured:
- Type check for IPluginHotkey
- Hotkey info initialization
- Thread-safe settings update under lock
- Window hotkey registration
- Plugin tracking
616-619
: LGTM!The method correctly returns hotkey-capable plugins while filtering out modified ones, consistent with other plugin getter methods.
828-833
: LGTM!Straightforward caching of plugin hotkey information.
835-857
: LGTM!The window hotkey initialization correctly:
- Filters for SearchWindow type hotkeys
- Retrieves configured or default hotkey settings
- Builds the hotkey-to-plugin mapping dictionary
859-879
: LGTM!The getter methods provide appropriate access to cached hotkey information with proper filtering.
881-903
: LGTM!The translation update logic correctly:
- Updates existing hotkey Name and Description
- Adds newly declared hotkeys
- Initializes if plugin wasn't previously in the cache
905-918
: LGTM!The GlobalPluginHotkey change flow is correct:
- Updates both metadata and settings
- Raises event for subscribers
- Uses direct access for clear failure modes (as per maintainer preference)
960-970
: LGTM!Clean event args class using modern C# primary constructor pattern with appropriate read-only properties.
- Added `PluginHotkeyInitialized` event in `PluginManager` to notify when plugin hotkeys are initialized. - Replaced `List` with `ConcurrentBag` for `_windowPluginHotkeys` to ensure thread-safe operations. - Enhanced `GetWindowPluginHotkeys` to support filtering by plugin ID. - Updated `HotKeyMapper` to handle `PluginHotkeyInitialized` and register hotkeys dynamically. - Refactored `WindowPluginHotkeyPair` and `GetRegisteredHotkeyData` to use `ConcurrentBag`. - Improved debug logging for hotkey initialization. - Refactored initialization of ActionContext hotkeys using modern C# collection syntax. - General code cleanup for readability and maintainability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 39 out of 40 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
Flow.Launcher/Helper/HotKeyMapper.cs:1
- [nitpick] Using discard
out var _
is appropriate here since the removed value is not needed. However, consider checking the return value ofTryRemove
to ensure the removal was successful before proceeding.
using System;
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
Simplified `PluginManager.cs` by removing unused `using` directives, including `System.Windows.Input` and several Flow Launcher-specific namespaces. Eliminated the instantiation of `KeyGestureConverter` in the `ChangePluginHotkey` method, reflecting a change in how hotkeys are processed. These changes reduce dependencies and streamline the codebase.
@coderabbitai full review
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
Flow.Launcher.Core/Plugin/PluginManager.cs (1)
942-978
: Remove unused KeyGestureConverter variable.Line 947 declares
var converter = new KeyGestureConverter();
but this variable is never used in the method.Apply this diff to remove the unused variable:
var oldHotkeyItem = plugin.PluginHotkeys.First(h => h.Id == pluginHotkey.Id); var settingHotkeyItem = Settings.GetPluginSettings(plugin.ID).pluginHotkeys.First(h => h.Id == pluginHotkey.Id); var oldHotkeyStr = settingHotkeyItem.Hotkey; - var converter = new KeyGestureConverter(); var oldHotkey = new HotkeyModel(oldHotkeyStr); var newHotkeyStr = newHotkey.ToString();
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
Flow.Launcher.Core/Plugin/PluginManager.cs
(6 hunks)Flow.Launcher/Helper/HotKeyMapper.cs
(4 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025年09月28日T03:57:43.995Z
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3770
File: Flow.Launcher/Helper/HotKeyMapper.cs:0-0
Timestamp: 2025年09月28日T03:57:43.995Z
Learning: In Flow.Launcher/Helper/HotKeyMapper.cs, the resource descriptions for arrow keys are intentionally swapped - Up/Down keys use "HotkeyLeftRightDesc" and Left/Right keys use "HotkeyUpDownDesc". This is documented with an inline comment and is intended behavior, not a bug.
Applied to files:
Flow.Launcher/Helper/HotKeyMapper.cs
🧬 Code graph analysis (1)
Flow.Launcher/Helper/HotKeyMapper.cs (2)
Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs (6)
RegisteredHotkeyData
(88-99)RegisteredHotkeyData
(130-141)RegisteredHotkeyData
(174-187)RegisteredHotkeyData
(221-234)SetHotkey
(240-243)ToString
(246-249)Flow.Launcher/HotkeyControl.xaml.cs (2)
SetHotkey
(287-316)SetHotkey
(340-343)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (7)
Flow.Launcher.Core/Plugin/PluginManager.cs (2)
36-37
: LGTM! Event declarations correctly implemented.The events are properly declared as
event Action<T>
following the resolution of past review feedback. This enforces add/remove semantics and prevents accidental overwriting of subscribers.
375-389
: LGTM! Plugin hotkey initialization is well-structured.The method correctly:
- Checks for IPluginHotkey implementation
- Initializes hotkey info and window hotkey mappings
- Uses a lock for thread-safe settings updates
- Populates
_hotkeyPlugins
(addressing the past critical issue)- Fires the
PluginHotkeyInitialized
event for downstream integrationFlow.Launcher/Helper/HotKeyMapper.cs (5)
34-127
: LGTM! Comprehensive initialization flow.The initialization sequence correctly:
- Sets up action context hotkeys for backward compatibility
- Builds a consolidated list of all hotkey types (system, Flow Launcher, result modifiers, custom query, and plugin hotkeys)
- Conditionally registers Dialog Jump based on
EnableDialogJump
setting- Subscribes to relevant events for dynamic updates
- Provides debug logging for visibility
This centralized approach improves maintainability over the previous scattered hotkey registration.
129-167
: LGTM! Plugin hotkey initialization handler is well-implemented.The handler properly:
- Retrieves global and window hotkeys for the initialized plugin
- Transforms them into
RegisteredHotkeyData
entries- Adds them to the centralized collection
- Registers them via
SetHotkey
- Provides per-plugin debug logging
This ensures plugin-declared hotkeys are seamlessly integrated into the main hotkey subsystem.
173-337
: LGTM! Event handlers provide proper dynamic hotkey management.The event handlers correctly:
- React to settings property changes and update corresponding hotkeys
- Handle custom plugin hotkey collection changes (Add/Remove/Replace)
- Process plugin hotkey changes by unregistering old and registering new hotkeys
- Differentiate between global and window plugin hotkeys (window hotkeys require rebuilding the registration from current PluginManager state)
This event-driven approach ensures hotkeys stay synchronized with user settings and plugin state.
401-613
: LGTM! Hotkey setting and removal logic is robust.The implementation correctly:
- Dispatches to type-specific methods (Global vs. SearchWindow)
- Handles Win keys via ChefKeys for global hotkeys
- Prevents duplicate window hotkey bindings while allowing ActionContext compatibility
- Restores ActionContext event bindings when removing window hotkeys (backward compatibility)
- Provides comprehensive error handling with user-facing messages
The duplicate check at lines 514-518 and ActionContext restoration at lines 602-603 maintain backward compatibility while supporting the new plugin hotkey system.
659-727
: LGTM! Command handlers are well-designed with proper validation.The command handlers correctly:
- Check
ShouldIgnoreHotkeys()
to respect game mode and fullscreen settings- Validate plugin state (disabled, modified) before invoking actions
- For window plugin hotkeys, verify plugin ID match, hotkey support in result, and action availability
- Hide main window after successful action invocation
- Fall back to ActionContext events when no plugin hotkey action is invoked (backward compatibility)
The comprehensive validation at lines 708-712 prevents errors from disabled plugins, modified plugins, or unsupported hotkeys on specific results.
✅ Actions performed
Full review triggered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
Flow.Launcher.Core/Resource/Internationalization.cs (1)
366-366
: Early return prevents processing remaining plugins.The
return
statement on line 366 exits the entire method when the first plugin withoutIPluginI18n
is encountered, preventing translation updates for subsequent plugins in the loop.Apply this diff to fix the issue:
foreach (var p in PluginManager.GetTranslationPlugins()) { - if (p.Plugin is not IPluginI18n pluginI18N) return; + if (p.Plugin is not IPluginI18n pluginI18N) continue; try {Flow.Launcher/HotkeyControl.xaml.cs (1)
318-322
: Delete() method should unregister plugin hotkeys from HotKeyMapper.The plugin hotkey setter (lines 216-225) stores the value locally without calling HotKeyMapper, and RefreshHotkeyInterface() (lines 245-249) only updates the UI display. When Delete() sets
Hotkey = ""
, it does not trigger any unregistration, leaving the hotkey registered with HotKeyMapper. This creates a dangling registration that persists until the application exits.Add explicit unregistration for GlobalPluginHotkey and WindowPluginHotkey types:
public void Delete() { + if (Type is HotkeyType.GlobalPluginHotkey or HotkeyType.WindowPluginHotkey) + { + if (!string.IsNullOrEmpty(Hotkey)) + { + HotKeyMapper.RemoveHotkey(Hotkey); + } + } Hotkey = ""; SetKeysToDisplay(new HotkeyModel(false, false, false, false, Key.None)); }Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs (1)
821-835
: Fix null/containment access on _lastExplorer to avoid exceptionsNavigateDialogPathAsync indexes _dialogJumpExplorers with a possibly null/absent _lastExplorer, risking ArgumentNullException/KeyNotFoundException and race issues.
Apply:
- string path; - lock (_lastExplorerLock) - { - path = _dialogJumpExplorers[_lastExplorer]?.GetExplorerPath(); - } + string path = null; + DialogJumpExplorerPair explorerLocal = null; + lock (_lastExplorerLock) + { + explorerLocal = _lastExplorer; + } + if (explorerLocal == null) + { + if (!RefreshLastExplorer()) + return false; + lock (_lastExplorerLock) + { + explorerLocal = _lastExplorer; + } + } + if (explorerLocal == null || !_dialogJumpExplorers.TryGetValue(explorerLocal, out var explorerWin) || explorerWin == null) + return false; + path = explorerWin.GetExplorerPath();
♻️ Duplicate comments (2)
Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml (1)
146-149
: Resolve default hotkey conflict with Settings windowOpenContextMenuHotkey sets DefaultHotkey="Ctrl+I", which conflicts with SettingWindowHotkey (also Ctrl+I). Remove or change the default to avoid collision.
Apply:
- <flowlauncher:HotkeyControl - DefaultHotkey="Ctrl+I" + <flowlauncher:HotkeyControl + DefaultHotkey="" Type="OpenContextMenuHotkey" ValidateKeyGesture="False" />Flow.Launcher.Infrastructure/UserSettings/Settings.cs (1)
761-763
: Ensure RegisteredHotkeys is cleared before repopulating (HotKeyMapper).This live collection should be cleared before rebuilds to avoid duplicates on reload/plugin changes. This was already raised earlier.
🧹 Nitpick comments (10)
Flow.Launcher.Plugin/ActionContext.cs (1)
10-10
: Deprecation correctly implements PR objectives.The
Obsolete
attribute properly marksActionContext
for deprecation in favor of the newIPluginHotkey
interface, aligning with the PR's goal to introduce a plugin-level hotkey system.Consider enhancing the deprecation message with:
- A link to migration documentation or examples
- A specific version or timeline for removal (e.g., "will be removed in v2.0")
Example:
-[Obsolete("ActionContext support is deprecated and will be removed in a future release. Please use IPluginHotkey instead.")] +[Obsolete("ActionContext support is deprecated and will be removed in v2.0. Please migrate to IPluginHotkey. See https://github.com/Flow-Launcher/Flow.Launcher/wiki/Plugin-Hotkeys for migration guide.")]Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs (1)
18-20
: Consider more explicit handling for root-level rename attempts.The fallback to
Path.GetPathRoot
whenfile.Directory
ordirectory.Parent
is null may not behave correctly for all path types (e.g., UNC paths, network drives). While this approach prevents null reference exceptions, it can produce unexpected results when attempting to rename items at the root level.Consider one of these approaches:
Option 1: Explicitly reject root-level renames with a clear error:
DirectoryInfo directory; var rootPath = Path.GetPathRoot(file.FullName); -if (string.IsNullOrEmpty(rootPath)) return; -directory = file.Directory ?? new DirectoryInfo(rootPath); +if (string.IsNullOrEmpty(rootPath)) +{ + throw new InvalidOperationException("Cannot determine root path for file"); +} +if (file.Directory == null) +{ + throw new InvalidOperationException("Cannot rename file at root level"); +} +directory = file.Directory;Option 2: Document the current behavior and add a comment explaining that root-level operations return silently:
+// Root-level items cannot be renamed - silently return var rootPath = Path.GetPathRoot(file.FullName); if (string.IsNullOrEmpty(rootPath)) return; directory = file.Directory ?? new DirectoryInfo(rootPath);
Also applies to: 37-39
Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml (1)
8-8
: Empty window title.The
Title
is set to an empty string. Consider setting it to a localized resource or binding it to a property in the code-behind that provides a meaningful title like "Rename File" or "Rename Folder".Flow.Launcher.Core/Plugin/PluginManager.cs (2)
836-858
: Consider defensive check for empty hotkey settings.At lines 847-848, an empty or whitespace
hotkeySetting
could lead to an invalidHotkeyModel
instance being created and added to the dictionary. While the maintainer previously declined a similar suggestion, consider whether validation is needed here to prevent potential runtime issues.
941-976
: Minor: use discard syntax for unused out parameter.At line 958, consider using
out _
instead ofout var _
for consistency with modern C# discard syntax.Apply this diff:
- _windowPluginHotkeys.TryRemove(oldHotkey, out var _); + _windowPluginHotkeys.TryRemove(oldHotkey, out _);Otherwise, the method correctly updates search window plugin hotkeys and maintains the dictionary state properly.
Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs (1)
237-245
: Unhook partially created WinEvent hooks on failureWhen any SetWinEventHook returns Null, you log and return without cleaning up other hooks, leaving dangling hooks.
Consider unhooking any non-null hooks before returning to keep state consistent.
Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs (1)
237-238
: Align property naming with conventions (or annotate for serialization)pluginHotkeys uses camelCase while other properties are PascalCase. Consider renaming to PluginHotkeys and, if persistence requires camelCase, annotate with JsonPropertyName.
Example:
[JsonPropertyName("pluginHotkeys")] public List<PluginHotkey> PluginHotkeys { get; set; } = new();Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml (1)
215-230
: Polish wording for rename dialog strings (optional)Consider clearer phrasing:
- <system:String x:Key="plugin_explorer_not_a_new_name">The given name: {0} was not new.</system:String> + <system:String x:Key="plugin_explorer_not_a_new_name">The provided name is unchanged: {0}.</system:String> - <system:String x:Key="plugin_explorer_field_may_not_be_empty">New file name should not be empty.</system:String> + <system:String x:Key="plugin_explorer_field_may_not_be_empty">The new name cannot be empty.</system:String> - <system:String x:Key="plugin_explorer_element_already_exists">There is already a file with the name: {0} in this location</system:String> + <system:String x:Key="plugin_explorer_element_already_exists">An item named {0} already exists in this location.</system:String>Flow.Launcher.Plugin/PluginHotkey.cs (1)
47-51
: Remove redundant initializer on HotkeyType to avoid double assignment.Ctor assigns HotkeyType; the default initializer is unnecessary and slightly misleading.
- public HotkeyType HotkeyType { get; } = HotkeyType.Global; + public HotkeyType HotkeyType { get; }Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs (1)
307-310
: Align enum naming with HotkeyType.SearchWindow.Consider renaming PluginWindowHotkey → PluginSearchWindowHotkey for consistency with HotkeyType.SearchWindow. Defer if it ripples broadly.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
Plugins/Flow.Launcher.Plugin.Explorer/Images/rename.png
is excluded by!**/*.png
📒 Files selected for processing (39)
Flow.Launcher.Core/Plugin/PluginManager.cs
(6 hunks)Flow.Launcher.Core/Resource/Internationalization.cs
(2 hunks)Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs
(2 hunks)Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs
(4 hunks)Flow.Launcher.Infrastructure/Hotkey/IHotkeySettings.cs
(2 hunks)Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs
(7 hunks)Flow.Launcher.Infrastructure/UserSettings/PluginSettings.cs
(2 hunks)Flow.Launcher.Infrastructure/UserSettings/Settings.cs
(4 hunks)Flow.Launcher.Plugin/ActionContext.cs
(1 hunks)Flow.Launcher.Plugin/Interfaces/IPluginHotkey.cs
(1 hunks)Flow.Launcher.Plugin/PluginHotkey.cs
(1 hunks)Flow.Launcher.Plugin/PluginMetadata.cs
(1 hunks)Flow.Launcher.Plugin/Result.cs
(2 hunks)Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs
(1 hunks)Flow.Launcher/Helper/HotKeyMapper.cs
(4 hunks)Flow.Launcher/HotkeyControl.xaml.cs
(3 hunks)Flow.Launcher/Languages/en.xaml
(1 hunks)Flow.Launcher/MainWindow.xaml
(0 hunks)Flow.Launcher/Resources/Pages/WelcomePage2.xaml
(0 hunks)Flow.Launcher/Resources/Pages/WelcomePage2.xaml.cs
(0 hunks)Flow.Launcher/Resources/Pages/WelcomePage3.xaml
(4 hunks)Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs
(0 hunks)Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs
(0 hunks)Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml
(5 hunks)Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs
(2 hunks)Flow.Launcher/ViewModel/MainViewModel.cs
(2 hunks)Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs
(2 hunks)Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs
(1 hunks)Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml
(2 hunks)Plugins/Flow.Launcher.Plugin.Explorer/Main.cs
(3 hunks)Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs
(1 hunks)Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs
(6 hunks)Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml
(1 hunks)Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs
(1 hunks)Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs
(2 hunks)Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs
(3 hunks)Plugins/Flow.Launcher.Plugin.Program/Main.cs
(2 hunks)Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs
(1 hunks)Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs
(1 hunks)
💤 Files with no reviewable changes (5)
- Flow.Launcher/Resources/Pages/WelcomePage2.xaml
- Flow.Launcher/MainWindow.xaml
- Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs
- Flow.Launcher/Resources/Pages/WelcomePage2.xaml.cs
- Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs
🧰 Additional context used
🧠 Learnings (8)
📚 Learning: 2025年09月28日T03:59:59.693Z
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3770
File: Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs:75-80
Timestamp: 2025年09月28日T03:59:59.693Z
Learning: In Flow.Launcher's Explorer plugin rename dialog (RenameFile.xaml.cs), the dialog should close unconditionally after calling RenameThing.Rename(), even on validation failures, because RenameThing.Rename() displays error messages via popup notifications. This is the intended UX design.
Applied to files:
Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml
Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs
Plugins/Flow.Launcher.Plugin.Explorer/Helper/RenameThing.cs
Plugins/Flow.Launcher.Plugin.Explorer/Main.cs
Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs
📚 Learning: 2025年06月24日T19:06:48.344Z
Learnt from: Koisu-unavailable
PR: Flow-Launcher/Flow.Launcher#3770
File: Flow.Launcher/ViewModel/MainViewModel.cs:0-0
Timestamp: 2025年06月24日T19:06:48.344Z
Learning: In Flow.Launcher's Explorer plugin results, the SubTitle property always contains the directory containing the file. For file results, Title contains the filename and SubTitle contains the parent directory. For directory results, SubTitle contains the directory path itself.
Applied to files:
Flow.Launcher/ViewModel/MainViewModel.cs
📚 Learning: 2025年09月28日T03:57:43.995Z
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3770
File: Flow.Launcher/Helper/HotKeyMapper.cs:0-0
Timestamp: 2025年09月28日T03:57:43.995Z
Learning: In Flow.Launcher/Helper/HotKeyMapper.cs, the resource descriptions for arrow keys are intentionally swapped - Up/Down keys use "HotkeyLeftRightDesc" and Left/Right keys use "HotkeyUpDownDesc". This is documented with an inline comment and is intended behavior, not a bug.
Applied to files:
Flow.Launcher/Helper/HotKeyMapper.cs
Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml
📚 Learning: 2025年03月28日T21:12:13.386Z
Learnt from: onesounds
PR: Flow-Launcher/Flow.Launcher#3394
File: Flow.Launcher/Themes/Darker Glass.xaml:134-141
Timestamp: 2025年03月28日T21:12:13.386Z
Learning: In Flow.Launcher, hotkey styling is implemented with a two-component structure: a Border element with style `ItemHotkeyBGStyle` that provides background and border styling, containing a TextBlock with style `ItemHotkeyStyle` that handles the text styling.
Applied to files:
Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml
📚 Learning: 2025年10月16日T10:48:30.551Z
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3854
File: Flow.Launcher/App.xaml.cs:252-275
Timestamp: 2025年10月16日T10:48:30.551Z
Learning: In Flow Launcher's App.xaml.cs, the plugin initialization block (lines 252-275) that includes PreStartPluginExecutablePathUpdate, PluginManager.LoadPlugins, PluginManager.InitializePluginsAsync, _mainVM.QueryResults(), and API.SaveAppAllSettings() does not require additional Task.Run wrappers or Dispatcher.InvokeAsync calls according to maintainer Jack251970, as the threading model is already safe as designed.
Applied to files:
Plugins/Flow.Launcher.Plugin.Explorer/Main.cs
Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs
📚 Learning: 2025年07月21日T09:19:49.684Z
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3854
File: Flow.Launcher/App.xaml.cs:246-262
Timestamp: 2025年07月21日T09:19:49.684Z
Learning: In Flow Launcher's App.xaml.cs, the asynchronous plugin initialization task (containing AbstractPluginEnvironment.PreStartPluginExecutablePathUpdate, PluginManager.LoadPlugins, PluginManager.InitializePluginsAsync, and AutoPluginUpdates) does not require additional try-catch error handling according to maintainer Jack251970, as these operations are designed to handle exceptions internally.
Applied to files:
Plugins/Flow.Launcher.Plugin.Explorer/Main.cs
📚 Learning: 2025年09月05日T11:56:27.267Z
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3593
File: Flow.Launcher/HotkeyControlDialog.xaml:6-6
Timestamp: 2025年09月05日T11:56:27.267Z
Learning: In Flow.Launcher's migration to iNKORE.UI.WPF.Modern UI framework, dialog resource keys like PopuBGColor, PopupButtonAreaBGColor, PopupButtonAreaBorderColor, and PopupTextColor are provided by the iNKORE.UI.WPF.Modern framework itself, not defined locally in the codebase theme files.
Applied to files:
Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs
📚 Learning: 2024年11月03日T07:40:11.014Z
Learnt from: Yusyuriv
PR: Flow-Launcher/Flow.Launcher#3057
File: Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs:0-0
Timestamp: 2024年11月03日T07:40:11.014Z
Learning: In Flow Launcher, when using Windows Forms dialogs (e.g., in `JsonRPCPluginSettings.cs`), path validation is enabled by default in `OpenFileDialog` and `FolderBrowserDialog`, preventing users from selecting invalid paths, but it's possible to opt out of this validation on individual dialogs.
Applied to files:
Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: gitStream.cm
- GitHub Check: gitStream.cm
- GitHub Check: gitStream.cm
- GitHub Check: build
🔇 Additional comments (38)
Flow.Launcher.Plugin/ActionContext.cs (2)
1-2
: LGTM!The
using System;
directive is correctly added to support theObsolete
attribute.
22-65
:SpecialKeyState
should NOT be deprecated—it has independent uses beyondActionContext
.The search results show that
SpecialKeyState
is actively used independent ofActionContext
:
- It's part of the public plugin API for global keyboard event callbacks (
RegisterGlobalKeyboardCallback
,RemoveGlobalKeyboardCallback
)- It's used in infrastructure code (GlobalHotkey.cs, PublicAPIInstance.cs)
- It appears in delegate signatures like
FlowLauncherGlobalKeyboardEventHandler
- Plugins directly reference its properties for keyboard state checks
Leave it as public without deprecation.
Flow.Launcher/Helper/HotKeyMapper.cs (3)
34-46
: LGTM! Proper initialization order and event subscription.The initialization sequence correctly sets up ActionContext hotkeys first, then registered hotkeys, and finally subscribes to change events. This ensures all hotkeys are properly registered before any dynamic updates can occur.
48-127
: LGTM! Comprehensive hotkey registration.The
InitializeRegisteredHotkeys
method properly builds the complete list of hotkeys from multiple sources (system defaults, editable hotkeys, result modifiers, and custom queries) and registers them appropriately. The special handling for DialogJump based on the enable setting is correct.
753-823
: Deprecation strategy is appropriate.The ActionContext compatibility layer is properly marked with
[Obsolete]
attributes and includes clear guidance to migrate to IPluginHotkey. Maintaining this compatibility layer during the transition period is a sound approach that prevents breaking existing plugins while encouraging migration.Plugins/Flow.Launcher.Plugin.Explorer/Search/Constants.cs (1)
20-20
: LGTM! Consistent constant definition.The
RenameImagePath
constant follows the established pattern for image path constants in this class.Flow.Launcher/Languages/en.xaml (1)
426-426
: LGTM! Localization string added for plugin hotkey feature.The new
pluginHotkey
resource string supports the plugin hotkey UI introduced in this PR.Flow.Launcher.Infrastructure/Hotkey/IHotkeySettings.cs (1)
1-16
: LGTM! Improved UI binding support.Changing
RegisteredHotkeys
fromList<RegisteredHotkeyData>
toObservableCollection<RegisteredHotkeyData>
enables automatic UI updates when hotkeys are added, removed, or modified. This is essential for the dynamic hotkey management UI introduced in this PR.Plugins/Flow.Launcher.Plugin.Explorer/Search/ResultManager.cs (1)
309-320
: LGTM! Public API exposure enables plugin hotkey integration.Changing
OpenFile
andOpenFolder
fromprivate static
topublic static
allows these methods to be invoked from plugin hotkeys and other external contexts, which aligns with the plugin hotkey framework introduced in this PR.Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs (1)
378-417
: LGTM! Robust validation helpers with proper reserved name handling.The new validation methods correctly handle:
- Null/whitespace checks first (preventing NRE)
- Windows reserved device names (CON, PRN, AUX, etc.)
- Invalid filename/path characters
- Path separators specifically excluded from directory names
The implementation properly strips file extensions before checking reserved names, preventing false negatives for names like "CON.txt".
Based on learnings: The past review comments about null handling, array optimization, and whitespace validation have been properly addressed in this implementation.
Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs (1)
424-432
: LGTM! Consistent hotkey metadata across plugin management results.The addition of
HotkeyIds = [0]
andContextData
to install, update, and uninstall results enables these results to participate in the new plugin hotkey system. The ContextData provides access to plugin website and source code URLs for hotkey actions to use (e.g., opening the plugin's website).Also applies to: 702-705, 815-822
Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml (4)
9-16
: Verify dialog sizing behavior.The combination of
Height="180"
(fixed),MaxWidth="600"
, andSizeToContent="Width"
may not provide the best user experience. Additionally, there's noMinWidth
specified, which could result in an overly narrow dialog for short filenames.Consider:
- Adding
MinWidth="400"
or similar to prevent the dialog from being too narrow- Testing the dialog with various filename lengths to ensure it looks good in all cases
Based on PR comments, the dialog window positioning (z-order) and ESC key handling have been flagged as issues. Please verify that:
- The dialog appears in the foreground when "Hide Flow Launcher when focus is lost" is disabled
- ESC key closes the dialog (this should be handled in the code-behind via PreviewKeyDown or similar)
37-57
: LGTM!The custom title bar close button implementation is correctly configured with proper visual feedback (opacity change when inactive).
60-87
: LGTM!The main content area is well-structured with proper bindings and event handlers for a good user experience (auto-selecting text on focus, keyboard handling).
89-112
: LGTM!The button area is properly structured with good visual hierarchy (accent style on the primary "Done" button) and uses localized resources.
Flow.Launcher.Core/Plugin/PluginManager.cs (6)
35-36
: LGTM!The plugin hotkey events are correctly declared using the
event
keyword, ensuring proper add/remove semantics and preventing accidental overwrites.
44-49
: LGTM!The plugin hotkey collections are properly initialized and the lock ensures thread-safe updates to the plugin hotkey info.
374-388
: LGTM!The
CheckPluginHotkeys
method correctly initializes plugin hotkey info, updates settings in a thread-safe manner, and properly populates the_hotkeyPlugins
collection (addressing the previously flagged critical issue).
617-620
: LGTM!The
GetHotkeyPlugins
method correctly filters and returns hotkey-capable plugins, properly excluding modified plugins from the results.
860-900
: LGTM!The hotkey info retrieval methods are well-implemented with clear logic for filtering by plugin ID when specified, and properly handle the case where all hotkeys should be returned.
926-939
: LGTM with note.The method updates global plugin hotkeys correctly. While
First()
could throw if the hotkey ID is not found, the maintainer has indicated a preference for this direct access approach as it provides clearer failure modes.Plugins/Flow.Launcher.Plugin.Program/Programs/UWPPackage.cs (1)
461-465
: LGTM!The
HotkeyIds
initialization with[0]
correctly integrates the UWP app results with the new hotkey system.Flow.Launcher.Plugin/PluginMetadata.cs (1)
155-158
: LGTM!The
PluginHotkeys
property is correctly added with proper XML documentation and initialization to an empty list.Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs (2)
13-14
: LGTM!The new using directives are appropriate for the RenameFile dialog functionality.
182-210
: Rename context menu item looks good with minor notes.The rename functionality is correctly implemented with proper type handling for folders and files. A few observations:
Placeholder icon (line 207): The comment notes this is a placeholder. Ensure the actual image is added before release or tracked in a follow-up issue.
No error handling: If
RenameFile
window construction fails orShowDialog()
throws, the exception will propagate. While this may be acceptable for internal dialogs, consider whether any specific error handling is needed.Return false (line 203): Correctly returns
false
to keep the Flow Launcher window visible, allowing the user to see the parent window while the rename dialog is open.Based on learnings, the dialog will close unconditionally after calling
RenameThing.Rename()
, which displays error messages via popup notifications.Please confirm the actual rename icon image is added or tracked for implementation.
Flow.Launcher.Plugin/Result.cs (2)
274-278
: LGTM!The
HotkeyIds
property is correctly added with proper XML documentation. The spelling issue from previous review ("registed" → "registered") has been fixed.
331-332
: LGTM!The
HotkeyIds
property is correctly included in theClone()
method, ensuring cloned results maintain their hotkey associations.Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs (1)
202-206
: LGTM!The
HotkeyIds
initialization with[0]
correctly integrates Win32 program results with the new hotkey system. The maintainer has previously indicated that using a literal0
is acceptable for this case.Flow.Launcher.Plugin/Interfaces/IPluginHotkey.cs (1)
1-16
: LGTM!The
IPluginHotkey
interface is well-defined with clear documentation. The method name spelling issue from previous review ("GetPuginHotkeys" → "GetPluginHotkeys") has been fixed. The interface correctly extendsIFeatures
and provides a clear contract for plugins supporting hotkeys.Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs (1)
73-102
: LGTM!The plugin hotkey implementation is correct and follows the established pattern. The null checks and early returns properly handle edge cases.
Plugins/Flow.Launcher.Plugin.Explorer/Main.cs (1)
135-276
: LGTM!The plugin hotkey implementation correctly handles various file operations with appropriate error handling and user feedback. The return values properly control window visibility after actions.
Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml.cs (1)
44-167
: LGTM!The dynamic plugin hotkey UI construction is well-structured with proper async/await patterns, thread-safe UI updates, and appropriate error handling. The fire-and-forget icon loading on line 106 is acceptable for improving perceived performance.
Plugins/Flow.Launcher.Plugin.Explorer/Views/RenameFile.xaml.cs (1)
1-97
: LGTM!The rename dialog implementation is correct with proper text selection logic, keyboard shortcuts, and follows the accepted design pattern of closing unconditionally after rename attempts (as errors are displayed via popups).
Based on learnings.
Flow.Launcher/Resources/Pages/WelcomePage3.xaml (1)
1-225
: All DynamicResource keys exist.
All resource keys referenced in WelcomePage3.xaml are defined in the language files.Flow.Launcher.Infrastructure/DialogJump/DialogJump.cs (1)
469-471
: Command exposure LGTMPublic IRelayCommand with lazy RelayCommand is appropriate for hotkey binding.
Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs (2)
52-55
: Useful additions: IsEmpty and EmptyThese improve state checks and default handling.
129-133
: Ensure ToKeyGesture is only called for valid hotkeysKeyGesture ctor can throw for unsupported combinations. Call Validate(true) upstream before converting, or wrap in try/catch where used.
Flow.Launcher.Infrastructure/Hotkey/RegisteredHotkeyData.cs (1)
245-249
: ToString() logic LGTM.Correctly outputs "None" when Hotkey is empty.
Simplify hotkey initialization by using the null-coalescing assignment operator (`??=`) to ensure `metadata.PluginHotkeys` is initialized to an empty collection if null. Add a call to `Clear()` to reset the collection before reuse. Replace `List<PluginHotkey>()` with shorthand `[]` syntax for creating empty collections, improving code readability and consistency.
Uh oh!
There was an error while loading. Please reload this page.
Context
Windows system has a default context menu for doing some operations to one specific object like exe file, pdf file, etc. And some operations can have some hotkeys like ctrl-c for copy, etc. Since Flow provides many results like them, we should add context menu for them.
Flow designed context menu is good, but currently, for hotkey event binding in context menu, Flow introduces action context (
ActionContext
parameter inAction
andAsyncAction
inResult class
) like ctrl, alt, win key with the enter hotkey event (this is the key to open the action in one result) which means Flow need to regard ctrl+enter key event as enter event so that ctrl key will be passed with enter key.This can lead many possible problems:
Considering these issues, I would like to deprecate the action context API and introduce
IPluginHotkey
interface which can help plugins to register any hotkeys for their results. Additionally, plugins can return results with their supported hotkeys. And we need to find a workaround to ensure the compatibility for action context and I think we can fully deprecate it in future.In future, if plugins can register their hotkeys and specific supported hotkeys for their results. And we can look forward to a command bar like what does as Raycast. It will tell users what commands are available for the selected result. And it looks very nice for me, and I really enjoy this design.
Changes:
IPluginHotkey
to register global & search window hotkeys for one plugins.Hotkey registration refactor for both global hotkeys and main window hotkeys (KeyBinding): Now we are using call back function to register / unregister global and main window hotkeys which can improve code quality (Put all codes related to hotkey in
HotkeyMapper.cs
)Redesign welcome page 3: Removed Ctrl+Enter & Ctrl+Shift+Enter hotkeys since it will be deprated API and added more preset hotkeys.
Resolve #1614.
TODOS:
HotKeyMapper.Initialize();
(Asynchronous Loading & Initialization Plugin Model to Improve Window Startup Speed #3854 ).Future:
Test: