Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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

Draft
Koisu-unavailable wants to merge 209 commits into Flow-Launcher:dev
base: dev
Choose a base branch
Loading
from Koisu-unavailable:rename-file

Conversation

Copy link

@Koisu-unavailable Koisu-unavailable commented Jun 24, 2025
edited by Jack251970
Loading

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 in Action and AsyncAction in Result 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:

  1. Code quality issue: All events will be put in one action delegate with if sentences to check the action context.
  2. Window hotkey binding complexity: Flow needs to bind many preset hotkeys like ctrl-enter which can lead to complexity to hotkey management.
  3. Plugin hotkey registration difficulty: Plugin can only register hotkeys to one results with enter key, and it cannot register other hotkeys like F2 or something else.
  4. Plugin result confusion: Using alt+enter or ctrl+enter will trigger enter event for those results without specific handler for action context.

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:

  • New Interface IPluginHotkey to register global & search window hotkeys for one plugins.
/// <summary>
/// Represent plugins that support global hotkey or search window hotkey.
/// </summary>
public interface IPluginHotkey : IFeatures
{
 /// <summary>
 /// Get the list of plugin hotkeys which will be registered in the settings page.
 /// </summary>
 /// <returns></returns>
 List<BasePluginHotkey> GetPuginHotkeys();
}
  • Plugin hotkey setting UI
image
  • Add an item to the context menu for the explorer plugin that allow you to rename files/directories. It opens a dialog.
image
  • Add a hotkey (defaults to F2) that also opens the dialog when a file is selected
image
  • 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.

image
  • Redesign hotkey page: Removed Ctrl+Enter & Ctrl+Shift+Enter hotkeys since it will be deprated API and added all Flow hotkeys.
image

Resolve #1614.

TODOS:

Future:

  • Command bar like Raycast with those commands (Not sure, need discussion)

Test:

  • Welcome page hotkey can be changed.
  • All preset hotkeys in Hotkey page can be changed.
  • Result modifier can work.
  • Custom Query Hotkey can be added, deleted and edited.
  • Plugin global and window hotkey can be registered and edited.
  • Original API ActionContext can work.
  • Dialog Jump hotkey can be changed / enabled / disabled.

Jack251970 reacted with thumbs up emoji
Koisu-unavailable and others added 23 commits June 19, 2025 14:33
Copy link

gitstream-cm bot commented Jun 24, 2025

🥷 Code experts: Jack251970, onesounds

Jack251970, onesounds have most 👩‍💻 activity in the files.
Jack251970, onesounds have most 🧠 knowledge in the files.

See details

Flow.Launcher.Infrastructure/UserSettings/Settings.cs

Activity based on git-commit:

Jack251970 onesounds
JUN 3 additions & 2 deletions
MAY 84 additions & 30 deletions 5 additions & 3 deletions
APR 34 additions & 83 deletions 104 additions & 38 deletions
MAR 142 additions & 94 deletions 10 additions & 0 deletions
FEB 10 additions & 4 deletions
JAN 17 additions & 4 deletions

Knowledge based on git-blame:
Jack251970: 31%
onesounds: 20%

Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs

Activity based on git-commit:

Jack251970 onesounds
JUN 45 additions & 2 deletions
MAY 4 additions & 1 deletions 5 additions & 0 deletions
APR 228 additions & 27 deletions
MAR
FEB 38 additions & 28 deletions
JAN 21 additions & 3 deletions

Knowledge based on git-blame:
Jack251970: 50%
onesounds: 1%

Flow.Launcher/HotkeyControl.xaml.cs

Activity based on git-commit:

Jack251970 onesounds
JUN
MAY 5 additions & 0 deletions
APR 11 additions & 15 deletions 3 additions & 0 deletions
MAR 150 additions & 37 deletions
FEB
JAN

Knowledge based on git-blame:
Jack251970: 44%
onesounds: 2%

Flow.Launcher/Languages/en.xaml

Activity based on git-commit:

Jack251970 onesounds
JUN 8 additions & 0 deletions 7 additions & 6 deletions
MAY 14 additions & 3 deletions 15 additions & 2 deletions
APR 22 additions & 21 deletions 45 additions & 23 deletions
MAR 67 additions & 42 deletions 8 additions & 3 deletions
FEB 15 additions & 9 deletions
JAN 1 additions & 0 deletions

Knowledge based on git-blame:
onesounds: 43%
Jack251970: 12%

Flow.Launcher/MainWindow.xaml

Activity based on git-commit:

Jack251970 onesounds
JUN
MAY 3 additions & 3 deletions 4 additions & 4 deletions
APR
MAR 42 additions & 26 deletions
FEB 1 additions & 1 deletions
JAN

Knowledge based on git-blame:
onesounds: 85%
Jack251970: 7%

Flow.Launcher/PublicAPIInstance.cs

Activity based on git-commit:

Jack251970 onesounds
JUN 54 additions & 22 deletions
MAY 124 additions & 131 deletions 190 additions & 74 deletions
APR 165 additions & 61 deletions
MAR 21 additions & 19 deletions
FEB 28 additions & 11 deletions
JAN 45 additions & 47 deletions

Knowledge based on git-blame:
Jack251970: 53%
onesounds: 8%

Flow.Launcher/SettingPages/Views/SettingsPaneHotkey.xaml

Activity based on git-commit:

Jack251970 onesounds
JUN
MAY 1 additions & 1 deletions 4 additions & 1 deletions
APR
MAR 14 additions & 28 deletions
FEB
JAN

Knowledge based on git-blame:
onesounds: 11%
Jack251970: 3%

Flow.Launcher/ViewModel/MainViewModel.cs

Activity based on git-commit:

Jack251970 onesounds
JUN 25 additions & 13 deletions
MAY 529 additions & 316 deletions 4 additions & 0 deletions
APR 35 additions & 28 deletions
MAR 695 additions & 628 deletions 293 additions & 201 deletions
FEB 63 additions & 21 deletions 23 additions & 25 deletions
JAN 17 additions & 21 deletions

Knowledge based on git-blame:
Jack251970: 41%
onesounds: 9%

Plugins/Flow.Launcher.Plugin.Explorer/ContextMenu.cs

Activity based on git-commit:

Jack251970 onesounds
JUN
MAY 23 additions & 0 deletions 3 additions & 3 deletions
APR 13 additions & 18 deletions
MAR
FEB
JAN 1 additions & 0 deletions

Knowledge based on git-blame:
Jack251970: 7%
onesounds: 4%

Plugins/Flow.Launcher.Plugin.Explorer/Languages/en.xaml

Activity based on git-commit:

Jack251970 onesounds
JUN 7 additions & 0 deletions 5 additions & 2 deletions
MAY 10 additions & 2 deletions
APR
MAR
FEB
JAN

Knowledge based on git-blame:
onesounds: 17%
Jack251970: 9%

Plugins/Flow.Launcher.Plugin.Explorer/Main.cs

Activity based on git-commit:

Jack251970 onesounds
JUN 4 additions & 4 deletions
MAY 0 additions & 1 deletions
APR
MAR
FEB
JAN

Knowledge based on git-blame:
Jack251970: 4%

To learn more about /:\ gitStream - Visit our Docs

Jack251970 and others added 2 commits October 16, 2025 20:07
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.
@Jack251970 Jack251970 marked this pull request as ready for review October 16, 2025 12:30
Copy link
Contributor

@Copilot Copilot AI left a 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 using out _ 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.

Jack251970 and others added 5 commits October 16, 2025 20:33
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>
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 in AddPluginToLists. Plugins implementing IPluginHotkey 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. If Initialize() 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 the SettingWindowHotkey card (line 167) specify DefaultHotkey="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

📥 Commits

Reviewing files that changed from the base of the PR and between 83c2edf and 79b0385.

📒 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 good

Exposing the dialog jump trigger as an IRelayCommand aligns it with the new hotkey mapper and the guard against EnableDialogJump 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 because RenameThing.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.
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between 79b0385 and f6759a5.

📒 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 in HotKeyMapper.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.

Jack251970 and others added 2 commits October 16, 2025 20:40
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 exceptions

OnToggleHotkey 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 in CheckPluginHotkeys) is resolved, this method will function as intended.


919-955: Remove unused KeyGestureConverter 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 init

Avoid 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, causing CheckPluginHotkeys 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 loads

Loaded 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 NRE

Fallback 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

📥 Commits

Reviewing files that changed from the base of the PR and between 83c2edf and d8a4bbc.

📒 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 LGTM

Required 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 to Settings.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 of First() 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 good

Properly guards ToggleFlowLauncher behind ShouldIgnoreHotkeys().


1858-1861: Accessor for SelectedResults is fine

Internal exposure is appropriate for in-assembly hotkey handling.

Flow.Launcher/Resources/Pages/WelcomePage3.xaml (1)

89-123: Hotkey help updates read well

Consistent 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 sound

API 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 correctly

Updating 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 good

Property-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.
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between d8a4bbc and 8c95ddc.

📒 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 and ConcurrentDictionary 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.
Copy link
Contributor

@Copilot Copilot AI left a 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 of TryRemove 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.
Copy link
Member

@coderabbitai full review

coderabbitai[bot] reacted with eyes emoji

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between dccdd95 and 811239b.

📒 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 integration
Flow.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.

Copy link
Contributor

coderabbitai bot commented Oct 16, 2025

✅ Actions performed

Full review triggered.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 without IPluginI18n 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 exceptions

NavigateDialogPathAsync 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 window

OpenContextMenuHotkey 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 marks ActionContext for deprecation in favor of the new IPluginHotkey 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 when file.Directory or directory.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 invalid HotkeyModel 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 of out 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 failure

When 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

📥 Commits

Reviewing files that changed from the base of the PR and between da4b961 and f8e588b.

⛔ 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 the Obsolete attribute.


22-65: SpecialKeyState should NOT be deprecated—it has independent uses beyond ActionContext.

The search results show that SpecialKeyState is actively used independent of ActionContext:

  • 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 from List<RegisteredHotkeyData> to ObservableCollection<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 and OpenFolder from private static to public 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] and ContextData 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", and SizeToContent="Width" may not provide the best user experience. Additionally, there's no MinWidth 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:

  1. The dialog appears in the foreground when "Hide Flow Launcher when focus is lost" is disabled
  2. 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:

  1. 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.

  2. No error handling: If RenameFile window construction fails or ShowDialog() throws, the exception will propagate. While this may be acceptable for internal dialogs, consider whether any specific error handling is needed.

  3. 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 the Clone() 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 literal 0 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 extends IFeatures 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 LGTM

Public IRelayCommand with lazy RelayCommand is appropriate for hotkey binding.

Flow.Launcher.Infrastructure/Hotkey/HotkeyModel.cs (2)

52-55: Useful additions: IsEmpty and Empty

These improve state checks and default handling.


129-133: Ensure ToKeyGesture is only called for valid hotkeys

KeyGesture 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.

@prlabeler prlabeler bot added the bug Something isn't working label Oct 16, 2025
@Jack251970 Jack251970 marked this pull request as draft October 16, 2025 14:33
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@jjw24 jjw24 jjw24 requested changes

@Jack251970 Jack251970 Jack251970 left review comments

@coderabbitai coderabbitai[bot] coderabbitai[bot] left review comments

Copilot code review Copilot Copilot left review comments

Requested changes must be addressed to merge this pull request.

Labels

30 min review bug Something isn't working Code Quality Code Refactor enhancement New feature or request

Projects

None yet

Milestone

Future

AltStyle によって変換されたページ (->オリジナル) /