<!-- 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
This PR fixes the migration of bookmarks from versions prior to 0.95,
resolving an issue where hotkeys and aliases wouldn’t persist on
bookmarks created with Command Palette 0.94 or earlier.
It removes ID auto-fixing from `BookmarkData` in favor of an explicit
migration step handled by `BookmarkManager`.
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
- [x] Closes: #42796
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **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
<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
## Summary of the Pull Request
This PR improves recognition and classification of bookmarks, allowing
CmdPal to recognize almost anything sensible a user can throw at
it—while being forgiving of common input issues (such as unquoted spaces
in paths, etc.).
Extended classification and exploration of edge cases also revealed
limitations in the current implementation, which reloaded all bookmarks
on every change. This caused visible UI lag and could lead to issues
like unintentionally adding the same bookmark multiple times.
### tl;dr
More details below
- Introduces `BookmarkManager` (async saves, thread-safe, immutable,
unique IDs, separate persistence).
- Adds `BookmarkResolver` (classification, Shell-like path/exe
resolution, better icons).
- `BookmarkListItem` now refreshes independently; Name is optional
(Shell fallback).
- Uses Shell API for user-friendly names and paths.
- Adds `IIconLocator`, protocol icon support, Steam custom icon,
fallback icons and improved `FaviconLoader` (handles redirects). Every
bookmark should now have icon, so we have consistent UI without gaps.
- Refactors placeholders (`IPlaceholderParser`), adds tests, restricts
names to `[a-zA-Z0-9_-]`, excludes GUIDs.
- Reorganizes structure, syncs icons/key chords with AllApps/Indexer.
- For web and protocol bookmarks URL-encodes placeholder values
- **Performance:** avoids full reloads, improves scalability, reduces UI
lag.
- **Breaking change:** stricter placeholder rules, bookmark command ids.
<img width="786" height="1392" alt="image"
src="https://github.com/user-attachments/assets/88d6617a-9f7c-47d1-bd60-80593fe414d3"
/>
<img width="786" height="1389" alt="image"
src="https://github.com/user-attachments/assets/8cdd3a09-73ae-439a-94ef-4e14d14c1ef3"
/>
<img width="896" height="461" alt="image"
src="https://github.com/user-attachments/assets/1f32e230-7d32-4710-b4c5-28e202c0e37b"
/>
<img width="862" height="391" alt="image"
src="https://github.com/user-attachments/assets/7649ce6a-3471-46f2-adc4-fb21bd4ecfed"
/>
<img width="844" height="356" alt="image"
src="https://github.com/user-attachments/assets/0c0b1941-fe5c-474e-94e9-de3817cb5470"
/>
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
- [x] Closes: #41705
- [x] Closes: #41892
- [x] Closes: #41872
- [x] Closes: #41545
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **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
## Detailed Description of the Pull Request / Additional comments
### Changes
- **Bookmark Manager**
- Introduces a `BookmarkManager` class that:
- Holds bookmarks in memory and saves changes asynchronously.
- Is safe to operate from multiple threads.
- Uses immutable data for transport.
- Separates the **persistence model** from in-memory data.
- Assigns explicit unique IDs to bookmarks.
- These IDs also serve as stable top-level command identifiers, enabling
aliases and shortcuts to be bound reliably.
- **Bookmark Resolver**
- Determines the type of a bookmark (`CommandKind`: file, web link,
command, etc.).
- Detects its target and parameters.
- Returns a `Classification` object containing all information needed to
present the bookmark to the user (icon, primary command, context menu
actions, etc.).
- For unquoted local paths, attempts to find the *longest viable
matching path* to a file or executable, automatically handling spaces in
paths (e.g., `C:\Program Files`).
- The resolution of executables from the command line now more closely
matches **Windows Shell** behavior.
- Users are more likely to get the correct result.
- Icons can be determined more reliably.
- **Bookmark List Items**
- Each top-level bookmark item (`BookmarkListItem`) is now responsible
for presenting itself.
- Items refresh their state independently on load or after changes.
- The **Name** field is now optional.
- If no explicit name is provided, a user-friendly fallback name is
computed automatically using the Shell API.
- Context actions are now more in line with **All Apps** and **Indexer**
built-in extensions, matching items, icons, and shortcuts (still a work
in progress).
- **Shell API Integration**
- Uses the Shell API to provide friendly names and paths for shell or
file system items, keeping the UI aligned with the OS.
- **Protocol and Icon Support**
- Adds `IIconLocator` and protocol icon support.
- Provides a custom icon for **Steam**, since Steam registers its
protocol to an executable not on the path (and the Steam protocol is
expected to be a common case).
- Adds `FaviconLoader` for web links.
- Can now follow redirects and retrieve the favicon even if the server
takes the request on a “sightseeing tour.”
- Provides **Fluent Segoe fallback icons** that match the bookmark
classification when no specific icon is available.
- **Refactors and Reorganization**
- Extracts `IPlaceholderParser` for testability and reusability.
- Renames `Bookmarks` → `BookmarksData` to prevent naming collisions.
- Reorganizes the structure (reducing root-level file clutter).
- Synchronizes icons and key chords with AllApps/Indexer.
- Refactors placeholder parsing logic and **adds tests** to improve
reliability.
- **Misc**
- Correctly URL-encodes placeholder values in Web URL or protocol
bookmarks.
---
### Performance Improvements
- Eliminates full reloads of all bookmarks on every change.
- Improves scalability when working with a large number of bookmarks.
- Independent refresh of list items reduces UI lag and improves
responsiveness.
- Asynchronous persistence prevents blocking the UI thread on saves.
---
### Breaking Changes
- **Placeholders**
- Placeholder names are now restricted to letters (`a–z`, `A–Z`), digits
(`0–9`), uderscore (`_`), hyphen (`-`).
- GUIDs are explicitly excluded as valid placeholders to prevent
collisions with shell IDs.
- When presented to the user, placeholders are considered
case-insensitive.
- ** Bookmark Top-Level Command
- **Bookmark Top-Level Command**
- IDs for bookmark commands are now based on a unique identifier.
- This breaks existing bindings to shortcuts and aliases.
- Newly created bindings will be stable regardless of changes to the
bookmark (name, address, or having placeholders).
-
<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
---------
Co-authored-by: Michael Jolley <mike@baldbeardedbuilder.com>
<!-- 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
1. Create Apps and Bookmarks ut project.
2. Refactor Apps and Bookmarks. And some interface in these extensions
to add a abstraction layer for testing purpose.
New interface list:
* ISettingsInterface
* IUWPApplication
* IAppCache
* IBookmarkDataSource
3. Add/Migrate some test case for Apps and Bookmarks extension
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
- [x] Closes: #41239#41240
- [x] **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
<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
---------
Co-authored-by: Yu Leng <yuleng@microsoft.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
What the title says. 😄
Rather than relying on the potentially overloaded `!=` or `==` operators
when checking for null, now we'll use the `is` expression (possibly
combined with the `not` operator) to ensure correct checking. Probably
overkill for many of these classes, but decided to err on the side of
consistency. Would matter more on classes that may be inherited or
extended.
Using `is` and `is not` will provide us a guarantee that no
user-overloaded equality operators (`==`/`!=`) is invoked when a
`expression is null` is evaluated.
In code form, changed all instances of:
```c#
something != null
something == null
```
to:
```c#
something is not null
something is null
```
The one exception was checking null on a `KeyChord`. `KeyChord` is a
struct which is never null so VS will raise an error when trying this
versus just providing a warning when using `keyChord != null`. In
reality, we shouldn't do this check because it can't ever be null. In
the case of a `KeyChord` it **would** be a `KeyChord` equivalent to:
```c#
KeyChord keyChord = new ()
{
Modifiers = 0,
Vkey = 0,
ScanCode = 0
};
```
_⚠️ targets #40427_
This is a different approach to #39059 that I was thinking about like a
month ago. It builds on the work from the rejuv'd run page (#39955) to
process the bookmark as an exe/path/url automatically.
I need to cross-check this with #39059 - I haven't cached that back in
since I got back from leave. I remember thinking that I wanted to try
this approach, but wasn't sure if it was right. More than anything, I
want to get it off my local PC and out for discussion
* We don't need to manually store the type anymore.
* breaking change: paths with a space do need to be wrapped in spaces
closes#38700
----
I accidentally destroyed #40430 with a fat-finger merge from #40427 into
it. This resurrects that PR
Just standardizing built-in extensions to use a `internal sealed class
Icons` for all their non-dynamic icons.
Looks like a LOT of changes, but it's icons all the way down.
* starting AoT flag push
* Few more
* bookmarks
* Really? The VM project compiles?
* Disable publish AOT before we really testing it.
---------
Co-authored-by: Mike Griese <migrie@microsoft.com>
Co-authored-by: Yu Leng (from Dev Box) <yuleng@microsoft.com>
**WARNING:** This PR will probably blow up all in-flight PRs
at some point in the early days of CmdPal, two of us created seperate
`Exts` and `exts` dirs. Depending on what the casing was on the branch
that you checked one of those out from, it'd get stuck like that on your
PC forever.
Windows didn't care, so we never noticed.
But GitHub does care, and now browsing the source on GitHub is basically
impossible.
Closes#38081