Skip to content

[Peek] Add CLI support for previewing multiple files/folders. Fix multiple CLI issues - #49724

Merged
Boliang Zhang (LegendaryBlair) merged 2 commits into
microsoft:mainfrom
daverayment:feat/peek-multi-image-cli
Oct 14, 2026
Merged

Boliang Zhang (LegendaryBlair) merged 2 commits into
microsoft:mainfrom
daverayment:feat/peek-multi-image-cli

Conversation

@daverayment

@daverayment Dave Rayment (daverayment) commented Aug 6, 2026 •

Copy link
Copy Markdown
Collaborator

Summary of the Pull Request

Previously, only one file could be previewed via the CLI. This adds support for multiple files, adds the ability to preview folders, and fixes a number of related issues with the CLI parameter parsing and validation code.

PR Checklist

Detailed Description of the Pull Request / Additional comments

1. Relative paths were not expanded into full paths

Submitting relative paths via the CLI passed file validation (the file was recognised as existing), but when passed to Shell APIs for opening, a full path is required, resulting in Peek showing an error message. This is fixed in the new InitializeFromCliPaths() method which validates the path arguments and expands out the valid ones into full paths.

2. Only a single file was supported

The previous CLI code

_selectedItem = new SelectedItemByPath(filePath);

meant that only 1 file was ever supported. Multiple files and/or folders were not.

To support multiple files via the command line this PR:

  1. Introduces a SelectedItemsByPaths class (notice the plurals), which inherits from SelectedItem and returns true from Matches() if the passed in path matches any of the paths passed in via the command line.
  2. Updates the Items field in the MainWindowViewModel from NeighboringItems to a more generic IReadOnlyList<IFileSystemItem>. NeighboringItems is closely tied to the Explorer Shell API query path and should not have been exposed directly to the model and front-end.
  3. Adds a HasMultipleItems property, replacing the NeighboringItemsQuery.IsMultipleFilesActivation, which controls whether the title bar shows the number of items and the current item number. This is similar to 2., where NeighboringItems and related types should not be part of the front-end.

Screenshot showing current item number and total items in title bar:

image

Folders are supported by this code in the new InitializeFromCliPaths(), which creates FileItem or FolderItem instances:

        foreach (var path in paths)
        {
            string name = Path.GetFileName(path);
            items.Add(Directory.Exists(path)
                ? new FolderItem(path, name, path)
                : new FileItem(path, name));
        }

Folder view from running PowerToys.Peek.UI.exe . (i.e. current folder):

image

3. Startup and validation fixes and increased robustness

  1. Added --runner-pid as a required parameter when supplying the Runner's process ID
    This fixes the issue where the last argument would always be interpreted as the Runner's process ID if it was numeric. With the addition of folder previews via the CLI, this was necessary, otherwise folders with numeric names could not be previewed if they were listed as the last argument.

  2. Validated Runner PID
    The numeric PID argument was always assumed to be correct previously. Providing an incorrect value manually would result in Peek opening, but it would not be linked to the Runner and could only be closed via a Task Manager kill. This PR validates that the PID is a running process and the linked exe is called "PowerToys.exe".

  3. Refactored startup code
    A new LaunchArgumentsClassifier class determines whether Peek is being opened via the Runner or the CLI, with new TryHandleRunnerLaunch() and TryHandleCliLaunch() methods validating and preparing the items for navigation.

4. Other changes

The more generic navigation of IFileSystemItems means that the CLI does not have to be treated as a special case in the MainWindowViewModel, so _isFromCli has been removed.

Validation Steps Performed

  1. New unit tests for the LaunchArgumentsClassifier code
  2. All new and existing unit tests pass:
image
  1. Manual tests:
  • Open Peek via Explorer selection of 1 file, 1 folder, and multiple files and folder(s) mix
  • Open Peek via CLI with 1 file, 1 folder, and multiple files and folder(s) mix
  • Test navigation for all above combinations
  • Confirm Peek process closes on CLI validation error
  • Confirm Peek process closes when opened via the CLI when user exits the application normally
  • Confirm file delete still works, independent of how Peek was opened

…ssues around command line and runner PID validation.
@daverayment Dave Rayment (daverayment) added the Product-Peek Refers to Peek Powertoys label Aug 6, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Folder display names and item-count visibility have unresolved edge-case defects, and the core CLI navigation path lacks automated coverage.

3 open findings
What changed in this PR

Adds robust Peek CLI support for multiple files and folders while distinguishing CLI launches from Runner launches.

Changes:

  • Adds explicit Runner PID parsing and validation.
  • Resolves CLI paths and supports mixed multi-item navigation.
  • Generalizes item-count UI state and adds argument-classifier tests.
File Description
src/​modules/​peek/​peek/​dllmain.cpp Passes the Runner PID using an explicit flag.
src/​modules/​peek/​Peek.UI/​PeekXAML/​MainWindow.xaml Binds title visibility to generalized item state.
src/​modules/​peek/​Peek.UI/​PeekXAML/​App.xaml.cs Classifies launches, validates PIDs, and resolves CLI paths.
src/​modules/​peek/​Peek.UI/​NativeMethods.txt Normalizes the final entry formatting.
src/​modules/​peek/​Peek.UI/​Models/​SelectedItemsByPaths.cs Represents multi-path CLI selections.
src/​modules/​peek/​Peek.UI/​MainWindowViewModel.cs Creates folder/file items and enables CLI navigation.
src/​modules/​peek/​Peek.Common/​Helpers/​LaunchArgumentsClassifier.cs Separates Runner and CLI argument forms.
src/​modules/​peek/​Peek.Common.UnitTests/​LaunchArgumentsClassifierTests.cs Tests launch argument classification.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

/// navigation, from either Explorer or CLI.
/// Controls the visibility of the index/total counter in the title bar.
/// </summary>
public bool HasMultipleItems => (Items?.Count ?? 0) > 1;
Comment on lines +189 to +192
string name = Path.GetFileName(path);
items.Add(Directory.Exists(path)
? new FolderItem(path, name, path)
: new FileItem(path, name));
Comment on lines +189 to +192
string name = Path.GetFileName(path);
items.Add(Directory.Exists(path)
? new FolderItem(path, name, path)
: new FileItem(path, name));
@LegendaryBlair
Boliang Zhang (LegendaryBlair) merged commit 6d81089 into microsoft:main Oct 14, 2026
5 checks passed
Boliang Zhang (LegendaryBlair) pushed a commit that referenced this pull request Oct 15, 2026
…tiple CLI issues (#49724)

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Previously, only one file could be previewed via the CLI. This adds
support for multiple files, adds the ability to preview folders, and
fixes a number of related issues with the CLI parameter parsing and
validation code.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #49667
- [x] Closes: #49666
- [x] Closes: #49665
- [x] Closes: #49664
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

### 1. Relative paths were not expanded into full paths
Submitting relative paths via the CLI passed file validation (the file
was recognised as existing), but when passed to Shell APIs for opening,
a full path is required, resulting in Peek showing an error message.
This is fixed in the new `InitializeFromCliPaths()` method which
validates the path arguments and expands out the valid ones into full
paths.

### 2. Only a single file was supported
The previous CLI code

```csharp
_selectedItem = new SelectedItemByPath(filePath);
```

meant that only 1 file was ever supported. Multiple files and/or folders
were not.

To support multiple files via the command line this PR:
1. Introduces a `SelectedItemsByPaths` class (notice the plurals), which
inherits from `SelectedItem` and returns `true` from `Matches()` if the
passed in path matches any of the paths passed in via the command line.
2. Updates the `Items` field in the `MainWindowViewModel` from
`NeighboringItems` to a more generic `IReadOnlyList<IFileSystemItem>`.
`NeighboringItems` is closely tied to the Explorer Shell API query path
and should not have been exposed directly to the model and front-end.
3. Adds a `HasMultipleItems` property, replacing the
`NeighboringItemsQuery.IsMultipleFilesActivation`, which controls
whether the title bar shows the number of items and the current item
number. This is similar to 2., where `NeighboringItems` and related
types should not be part of the front-end.

Screenshot showing current item number and total items in title bar:

<img width="671" height="731" alt="image"
src="https://github.com/user-attachments/assets/d4d724fc-728e-40d4-92cc-3a70f40f10f9"
/>

Folders are supported by this code in the new
`InitializeFromCliPaths()`, which creates `FileItem` or `FolderItem`
instances:

```csharp
        foreach (var path in paths)
        {
            string name = Path.GetFileName(path);
            items.Add(Directory.Exists(path)
                ? new FolderItem(path, name, path)
                : new FileItem(path, name));
        }
```

Folder view from running `PowerToys.Peek.UI.exe .` (i.e. current
folder):

<img width="756" height="575" alt="image"
src="https://github.com/user-attachments/assets/d834c12d-1e3e-423f-ad5b-0d2f93e55510"
/>

### 3. Startup and validation fixes and increased robustness

1. **Added `--runner-pid` as a required parameter when supplying the
Runner's process ID**
This fixes the issue where the last argument would always be interpreted
as the Runner's process ID if it was numeric. With the addition of
folder previews via the CLI, this was necessary, otherwise folders with
numeric names could not be previewed if they were listed as the last
argument.

2. **Validated Runner PID**
The numeric PID argument was always assumed to be correct previously.
Providing an incorrect value manually would result in Peek opening, but
it would not be linked to the Runner and could only be closed via a Task
Manager kill. This PR validates that the PID is a running process and
the linked exe is called "PowerToys.exe".

3. **Refactored startup code**
A new `LaunchArgumentsClassifier` class determines whether Peek is being
opened via the Runner or the CLI, with new `TryHandleRunnerLaunch()` and
`TryHandleCliLaunch()` methods validating and preparing the items for
navigation.

### 4. Other changes
The more generic navigation of `IFileSystemItem`s means that the CLI
does not have to be treated as a special case in the
MainWindowViewModel, so `_isFromCli` has been removed.

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
1. New unit tests for the `LaunchArgumentsClassifier` code
2. All new and existing unit tests pass:

<img width="391" height="137" alt="image"
src="https://github.com/user-attachments/assets/3b6a3148-9043-42b5-a2d8-69feaa6aed32"
/>

3. Manual tests:
- Open Peek via Explorer selection of 1 file, 1 folder, and multiple
files and folder(s) mix
- Open Peek via CLI with 1 file, 1 folder, and multiple files and
folder(s) mix
- Test navigation for all above combinations
- Confirm Peek process closes on CLI validation error
- Confirm Peek process closes when opened via the CLI when user exits
the application normally
- Confirm file delete still works, independent of how Peek was opened

(cherry picked from commit 6d81089)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

0.102 Area-CLI Issues related to PowerToys command-line interfaces Product-Peek Refers to Peek Powertoys

Projects

None yet

4 participants