mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-02-21 02:30:04 +01:00
Compare commits
20 Commits
niels9001/
...
dev/mjolle
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7f8ac0b06 | ||
|
|
09bdbfac38 | ||
|
|
12bfa13b72 | ||
|
|
54d1ab3b70 | ||
|
|
82671661a8 | ||
|
|
99a6c8c74e | ||
|
|
285183899f | ||
|
|
d3d39f91dc | ||
|
|
10f953f684 | ||
|
|
54c5d7b55d | ||
|
|
94a699d00b | ||
|
|
fa4bc0a397 | ||
|
|
303be86d86 | ||
|
|
ec423177da | ||
|
|
f78ec0c8e5 | ||
|
|
a1e8b6aca9 | ||
|
|
81aeb74fda | ||
|
|
b5ae2efc0d | ||
|
|
ba9585a663 | ||
|
|
5f273c7be6 |
@@ -1,5 +1,5 @@
|
||||
# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2
|
||||
# Reference: https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md#getting-started
|
||||
# Reference: https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md#compiling-powertoys
|
||||
properties:
|
||||
resources:
|
||||
- resource: Microsoft.Windows.Settings/WindowsSettings
|
||||
@@ -13,11 +13,11 @@ properties:
|
||||
- resource: Microsoft.WinGet.DSC/WinGetPackage
|
||||
id: vsPackage
|
||||
directives:
|
||||
description: Install Visual Studio 2026 Enterprise (Any edition will work)
|
||||
description: Install Visual Studio 2022 Enterprise (Any edition will work)
|
||||
# Requires elevation for the set operation
|
||||
securityContext: elevated
|
||||
settings:
|
||||
id: Microsoft.VisualStudio.Enterprise
|
||||
id: Microsoft.VisualStudio.2022.Enterprise
|
||||
source: winget
|
||||
- resource: Microsoft.VisualStudio.DSC/VSComponents
|
||||
dependsOn:
|
||||
@@ -29,7 +29,7 @@ properties:
|
||||
securityContext: elevated
|
||||
settings:
|
||||
productId: Microsoft.VisualStudio.Product.Enterprise
|
||||
channelId: VisualStudio.18.Release
|
||||
channelId: VisualStudio.17.Release
|
||||
vsConfigFile: '${WinGetConfigRoot}\..\.vsconfig'
|
||||
configurationVersion: 0.2.0
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2
|
||||
# Reference: https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md#getting-started
|
||||
# Reference: https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md#compiling-powertoys
|
||||
properties:
|
||||
resources:
|
||||
- resource: Microsoft.Windows.Settings/WindowsSettings
|
||||
@@ -13,11 +13,11 @@ properties:
|
||||
- resource: Microsoft.WinGet.DSC/WinGetPackage
|
||||
id: vsPackage
|
||||
directives:
|
||||
description: Install Visual Studio 2026 Professional (Any edition will work)
|
||||
description: Install Visual Studio 2022 Professional (Any edition will work)
|
||||
# Requires elevation for the set operation
|
||||
securityContext: elevated
|
||||
settings:
|
||||
id: Microsoft.VisualStudio.Professional
|
||||
id: Microsoft.VisualStudio.2022.Professional
|
||||
source: winget
|
||||
- resource: Microsoft.VisualStudio.DSC/VSComponents
|
||||
dependsOn:
|
||||
@@ -29,7 +29,7 @@ properties:
|
||||
securityContext: elevated
|
||||
settings:
|
||||
productId: Microsoft.VisualStudio.Product.Professional
|
||||
channelId: VisualStudio.18.Release
|
||||
channelId: VisualStudio.17.Release
|
||||
vsConfigFile: '${WinGetConfigRoot}\..\.vsconfig'
|
||||
configurationVersion: 0.2.0
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2
|
||||
# Reference: https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md#getting-started
|
||||
# Reference: https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md#compiling-powertoys
|
||||
properties:
|
||||
resources:
|
||||
- resource: Microsoft.Windows.Settings/WindowsSettings
|
||||
@@ -13,11 +13,11 @@ properties:
|
||||
- resource: Microsoft.WinGet.DSC/WinGetPackage
|
||||
id: vsPackage
|
||||
directives:
|
||||
description: Install Visual Studio 2026 Community (Any edition will work)
|
||||
description: Install Visual Studio 2022 Community (Any edition will work)
|
||||
# Requires elevation for the set operation
|
||||
securityContext: elevated
|
||||
settings:
|
||||
id: Microsoft.VisualStudio.Community
|
||||
id: Microsoft.VisualStudio.2022.Community
|
||||
source: winget
|
||||
- resource: Microsoft.VisualStudio.DSC/VSComponents
|
||||
dependsOn:
|
||||
@@ -29,7 +29,7 @@ properties:
|
||||
securityContext: elevated
|
||||
settings:
|
||||
productId: Microsoft.VisualStudio.Product.Community
|
||||
channelId: VisualStudio.18.Release
|
||||
channelId: VisualStudio.17.Release
|
||||
vsConfigFile: '${WinGetConfigRoot}\..\.vsconfig'
|
||||
configurationVersion: 0.2.0
|
||||
|
||||
|
||||
@@ -64,7 +64,6 @@
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<BuildStlModules>false</BuildStlModules>
|
||||
<AdditionalOptions>/await %(AdditionalOptions)</AdditionalOptions>
|
||||
<!-- TODO: _SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING for compatibility with VS 17.8. Check if we can remove. -->
|
||||
<PreprocessorDefinitions>_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<!-- CLR + CFG are not compatible >:{ -->
|
||||
|
||||
@@ -196,6 +196,10 @@
|
||||
<Folder Name="/modules/CommandPalette/">
|
||||
<Project Path="src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj" Id="5f63c743-f6ce-4dba-a200-2b3f8a14e8c2" />
|
||||
<Project Path="src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.vcxproj" Id="0adeb797-c8c7-4ffa-acd5-2af6cad7ecd8" />
|
||||
<Project Path="src/modules/cmdpal/Microsoft.CmdPal.Common/Microsoft.CmdPal.Common.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/modules/CommandPalette/Built-in Extensions/">
|
||||
<Project Path="src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Microsoft.CmdPal.Ext.Apps.csproj">
|
||||
@@ -275,16 +279,6 @@
|
||||
<Deploy />
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/modules/CommandPalette/Core/">
|
||||
<Project Path="src/modules/cmdpal/Core/Microsoft.CmdPal.Core.Common/Microsoft.CmdPal.Core.Common.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/cmdpal/Core/Microsoft.CmdPal.Core.ViewModels/Microsoft.CmdPal.Core.ViewModels.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/modules/CommandPalette/Extension SDK/">
|
||||
<Project Path="src/modules/cmdpal/extensionsdk/Microsoft.CommandPalette.Extensions.Toolkit/Microsoft.CommandPalette.Extensions.Toolkit.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
### Building PowerToys Locally
|
||||
|
||||
#### One stop script for building installer
|
||||
1. Open `Developer PowerShell for VS`.
|
||||
1. Open `Developer Powershell for VS 2022` or `Developer PowerShell for VS` for VS 2026.
|
||||
2. Run tools\build\build-installer.ps1
|
||||
> For the first-time setup, please run the installer as an administrator. This ensures that the Wix tool can move wix.target to the desired location and trust the certificate used to sign the MSIX packages.
|
||||
|
||||
@@ -109,7 +109,7 @@ dotnet tool install --global wix --version 5.0.2
|
||||
|
||||
##### From the command line
|
||||
|
||||
1. From the start menu, open a `Developer Command Prompt for VS`
|
||||
1. From the start menu, open a `Developer Command Prompt for VS 2022` or `Developer Command Prompt for VS`
|
||||
1. Ensure `nuget.exe` is in your `%path%`
|
||||
1. In the repo root, run these commands:
|
||||
|
||||
@@ -140,7 +140,7 @@ If you prefer, you can alternatively build prerequisite projects for the install
|
||||
|
||||
The resulting installer will be available in the `installer\PowerToysSetupVNext\x64\Release\` folder.
|
||||
|
||||
To build the installer from the command line, run `Developer Command Prompt for VS` in admin mode and execute the following commands. The generated installer package will be located at `\installer\PowerToysSetupVNext\{platform}\Release\MachineSetup`.
|
||||
To build the installer from the command line, run `Developer Command Prompt for VS 2022` or `Developer Command Prompt for VS` in admin mode and execute the following commands. The generated installer package will be located at `\installer\PowerToysSetupVNext\{platform}\Release\MachineSetup`.
|
||||
|
||||
```
|
||||
git clean -xfd -e *exe -- .\installer\
|
||||
|
||||
@@ -15,7 +15,7 @@ Before you can start debugging PowerToys, you need to set up your development en
|
||||
|
||||
You can build the entire solution from the command line, which is sometimes faster than building within Visual Studio:
|
||||
|
||||
1. Open `Developer Command Prompt for VS`
|
||||
1. Open `Developer Command Prompt for VS 2022` or `Developer Command Prompt for VS`
|
||||
2. Navigate to the repository root directory
|
||||
3. Run the following command(don't forget to set the correct platform):
|
||||
```pwsh
|
||||
@@ -105,7 +105,7 @@ If you encounter build errors about missing image files (e.g., `.png`, `.ico`, o
|
||||
|
||||
1. **Clean the solution in Visual Studio**: Build > Clean Solution
|
||||
|
||||
Or from the command line (`Developer Command Prompt for VS`):
|
||||
Or from the command line (Developer Command Prompt for VS 2022 or Developer Command Prompt for VS):
|
||||
```pwsh
|
||||
msbuild PowerToys.slnx /t:Clean /p:Platform=x64 /p:Configuration=Debug
|
||||
```
|
||||
|
||||
@@ -15,27 +15,9 @@ VS Code extensions Needed:
|
||||
---
|
||||
|
||||
## Building in VS Code
|
||||
### Configure Developer PowerShell for VS for more convenient development experience in VS Code
|
||||
1. Configure profile in settings, entry: `terminal.integrated.profiles.windows`
|
||||
2. Add below config as entry (choose VS 2026 or VS 2022 based on your installation):
|
||||
|
||||
**For Visual Studio 2026 (recommended):**
|
||||
```json
|
||||
"Developer PowerShell for VS": {
|
||||
// Configure based on your preference
|
||||
"path": "C:\\Program Files\\WindowsApps\\Microsoft.PowerShell_7.5.2.0_arm64__8wekyb3d8bbwe\\pwsh.exe",
|
||||
"args": [
|
||||
"-NoExit",
|
||||
"-Command",
|
||||
"& {",
|
||||
"$orig = Get-Location;",
|
||||
// Adjust path based on your edition (Community/Professional/Enterprise)
|
||||
"& 'C:\\Program Files\\Microsoft Visual Studio\\18\\Enterprise\\Common7\\Tools\\Launch-VsDevShell.ps1';",
|
||||
"Set-Location $orig",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
```
|
||||
### Configure Developer Powershell for VS 2022 or Developer Powershell for VS for more convenient dev in vscode.
|
||||
1. Configure profile in in settings, entry: "terminal.integrated.profiles.windows"
|
||||
2. Add below config as entry (choose VS 2022 or VS 2026 based on your installation):
|
||||
|
||||
**For Visual Studio 2022:**
|
||||
```json
|
||||
@@ -55,6 +37,24 @@ VS Code extensions Needed:
|
||||
},
|
||||
```
|
||||
|
||||
**For Visual Studio 2026:**
|
||||
```json
|
||||
"Developer PowerShell for VS": {
|
||||
// Configure based on your preference
|
||||
"path": "C:\\Program Files\\WindowsApps\\Microsoft.PowerShell_7.5.2.0_arm64__8wekyb3d8bbwe\\pwsh.exe",
|
||||
"args": [
|
||||
"-NoExit",
|
||||
"-Command",
|
||||
"& {",
|
||||
"$orig = Get-Location;",
|
||||
// Adjust path based on your edition (Community/Professional/Enterprise)
|
||||
"& 'C:\\Program Files\\Microsoft Visual Studio\\18\\Enterprise\\Common7\\Tools\\Launch-VsDevShell.ps1';",
|
||||
"Set-Location $orig",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
```
|
||||
|
||||
3. [Optional] Set your Developer PowerShell profile as the default, so that you can get a deep integration with vscode coding agent.
|
||||
|
||||
4. Now you can build with plain `msbuild` or configure tasks.json in below section.
|
||||
|
||||
@@ -12,11 +12,22 @@ A PowerToy module is a self-contained utility integrated into the PowerToys ecos
|
||||
|
||||
### Requirements
|
||||
|
||||
Follow the [Getting Started](../readme.md#getting-started) guide to set up your development environment, then [validate that you are able to build and run](debugging.md) `PowerToys.slnx`.
|
||||
- [Visual Studio 2026](https://visualstudio.microsoft.com/downloads/) and the following workloads/individual components:
|
||||
- Desktop Development with C++
|
||||
- WinUI application development
|
||||
- .NET desktop development
|
||||
- Windows 10 SDK (10.0.22621.0)
|
||||
- Windows 11 SDK (10.0.26100.3916)
|
||||
- .NET 8 SDK
|
||||
- Fork the [PowerToys repository](https://github.com/microsoft/PowerToys/tree/main) locally
|
||||
- [Validate that you are able to build and run](https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/development/debugging.md) `PowerToys.slnx`.
|
||||
|
||||
Optional:
|
||||
- [WiX v5 toolset](https://github.com/microsoft/PowerToys/tree/main) for the installer
|
||||
|
||||
> [!NOTE]
|
||||
> To ensure all the correct VS Workloads are installed, use [the WinGet configuration files](https://github.com/microsoft/PowerToys/tree/e13d6a78aafbcf32a4bb5f8581d041e1d057c3f1/.config) in the project repository. (Use the one that matches your VS distribution. ie: VS Community would use `configuration.winget`)
|
||||
|
||||
### Folder structure
|
||||
|
||||
```
|
||||
|
||||
@@ -152,7 +152,7 @@ FancyZones is divided into several projects:
|
||||
## Development Environment Setup
|
||||
|
||||
### Prerequisites
|
||||
- Visual Studio 2026 (or 2022 17.4+): Required for building and debugging
|
||||
- Visual Studio 2022 or 2026: Required for building and debugging
|
||||
- Windows 10 SDK: Ensure the latest version is installed
|
||||
- PowerToys Repository: Clone from GitHub
|
||||
|
||||
@@ -183,7 +183,7 @@ FancyZones is divided into several projects:
|
||||
## Debugging
|
||||
|
||||
### Setup for Debugging
|
||||
1. In Visual Studio, set FancyZonesEditor as the startup project
|
||||
1. In Visual Studio 2022 or 2026, set FancyZonesEditor as the startup project
|
||||
2. Set breakpoints in the code where needed
|
||||
3. Click Run to start debugging
|
||||
|
||||
|
||||
@@ -2,31 +2,91 @@
|
||||
|
||||
Welcome to the PowerToys developer documentation. This documentation provides information for developers who want to contribute to PowerToys or understand how it works.
|
||||
|
||||
## Getting Started
|
||||
## Core Architecture
|
||||
|
||||
### Prerequisites
|
||||
- [Architecture Overview](core/architecture.md) - Overview of the PowerToys architecture and module interface
|
||||
- [Runner and System tray](core/runner.md) - Details about the PowerToys Runner process
|
||||
- [Settings](core/settings/readme.md) - Documentation on the settings system
|
||||
- [Installer](core/installer.md) - Information about the PowerToys installer
|
||||
- [Modules](modules/readme.md) - Documentation for individual PowerToys modules
|
||||
|
||||
1. Windows 10 April 2018 Update (version 1803) or newer
|
||||
1. [Visual Studio 2026](https://visualstudio.microsoft.com/downloads/) (recommended) or Visual Studio 2022 17.4+ with the following workloads/components:
|
||||
- Desktop Development with C++
|
||||
- WinUI application development
|
||||
- .NET desktop development
|
||||
- Windows 10 SDK (10.0.22621.0)
|
||||
- Windows 11 SDK (10.0.26100.3916)
|
||||
1. .NET 8 SDK
|
||||
1. Enable long paths in Windows (see [Enable Long Paths](https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation#enabling-long-paths-in-windows-10-version-1607-and-later) for details)
|
||||
## Common Components
|
||||
|
||||
> **Tip:** You can install Visual Studio with all required workloads automatically using the [WinGet configuration files](https://github.com/microsoft/PowerToys/tree/main/.config) in the repository:
|
||||
> ```powershell
|
||||
> winget configure .config\configuration.winget
|
||||
> ```
|
||||
> Pick the file that matches your VS edition (e.g., `configuration.vsProfessional.winget` or `configuration.vsEnterprise.winget`).
|
||||
- [Context Menu Handlers](common/context-menus.md) - How PowerToys implements and registers Explorer context menu handlers
|
||||
- [Monaco Editor](common/monaco-editor.md) - How PowerToys uses the Monaco code editor component across modules
|
||||
- [Logging and Telemetry](development/logging.md) - How to use logging and telemetry
|
||||
- [Localization](development/localization.md) - How to support multiple languages
|
||||
|
||||
### Fork, Clone, and Set Up
|
||||
## Development Guidelines
|
||||
|
||||
- [Coding Guidelines](development/guidelines.md) - Development guidelines and best practices
|
||||
- [Coding Style](development/style.md) - Code formatting and style conventions
|
||||
- [UI Testing](development/ui-tests.md) - How to write UI tests for PowerToys
|
||||
- [Debugging](development/debugging.md) - Techniques for debugging PowerToys
|
||||
|
||||
## Tools
|
||||
|
||||
- [Tools Overview](tools/readme.md) - Overview of tools in PowerToys
|
||||
- [Build Tools](tools/build-tools.md) - Tools that help building PowerToys
|
||||
- [Bug Report Tool](tools/bug-report-tool.md) - Tool for collecting logs and system information
|
||||
- [Debugging Tools](tools/debugging-tools.md) - Specialized tools for debugging
|
||||
- [Fuzzing Testing](tools/fuzzingtesting.md) - How to implement and run fuzz testing for PowerToys modules
|
||||
|
||||
## Processes
|
||||
|
||||
- [Release Process](processes/release-process.md) - How PowerToys releases are prepared and published
|
||||
- [Update Process](processes/update-process.md) - How PowerToys updates work
|
||||
- [GPO Implementation](processes/gpo.md) - Group Policy Objects implementation details
|
||||
|
||||
## Other Resources
|
||||
|
||||
- [aka.ms links](akaLinks.md) - List of short links
|
||||
- [Issue/PR commands](commands.md) - Special commands for managing issues and pull requests
|
||||
|
||||
## Fork, Clone, Branch and Create your PR
|
||||
|
||||
Once you've discussed your proposed feature/fix/etc. with a team member, and an approach or a spec has been written and approved, it's time to start development:
|
||||
|
||||
1. Fork the repo on GitHub if you haven't already
|
||||
1. Clone your fork locally
|
||||
1. Run the automated setup script (**recommended**):
|
||||
1. Create a feature branch
|
||||
1. Work on your changes
|
||||
1. Create a [Draft Pull Request (PR)](https://github.blog/2019-02-14-introducing-draft-pull-requests/)
|
||||
1. When ready, mark your PR as "ready for review".
|
||||
|
||||
## Rules
|
||||
|
||||
- **Follow the pattern of what you already see in the code.**
|
||||
- [Coding style](development/style.md).
|
||||
- Try to package new functionality/components into libraries that have nicely defined interfaces.
|
||||
- Package new functionality into classes or refactor existing functionality into a class as you extend the code.
|
||||
- When adding new classes/methods/changing existing code, add new unit tests or update the existing tests.
|
||||
|
||||
## GitHub Workflow
|
||||
|
||||
- Before starting to work on a fix/feature, make sure there is an open issue to track the work.
|
||||
- Add the `In progress` label to the issue, if not already present. Also add a `Cost-Small/Medium/Large` estimate and make sure all appropriate labels are set.
|
||||
- If you are a community contributor, you will not be able to add labels to the issue; in that case just add a comment saying that you have started work on the issue and try to give an estimate for the delivery date.
|
||||
- If the work item has a medium/large cost, using the markdown task list, list each sub item and update the list with a check mark after completing each sub item.
|
||||
- **Before opening a PR, ensure your changes build successfully locally and functionality tests pass.** This is especially important for AI-assisted (vibe coding) contributions—always verify AI-generated code works as intended. Exploratory PRs or draft PRs for discussion are exceptions.
|
||||
- When opening a PR, follow the PR template.
|
||||
- When you'd like the team to take a look (even if the work is not yet fully complete) mark the PR as 'Ready For Review' so that the team can review your work and provide comments, suggestions, and request changes. It may take several cycles, but the end result will be solid, testable, conformant code that is safe for us to merge.
|
||||
- When the PR is approved, let the owner of the PR merge it. For community contributions, the reviewer who approved the PR can also merge it.
|
||||
- Use the `Squash and merge` option to merge a PR. If you don't want to squash it because there are logically different commits, use `Rebase and merge`.
|
||||
- Close issues automatically when referenced in a PR. You can use [closing keywords](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) in the body of the PR to have GitHub automatically link your PR to the issue.
|
||||
|
||||
## Compiling PowerToys
|
||||
|
||||
### Prerequisites for Compiling PowerToys
|
||||
|
||||
1. Windows 10 April 2018 Update (version 1803) or newer
|
||||
1. Visual Studio Community/Professional/Enterprise 2022 17.4 or newer, or Visual Studio 2026
|
||||
1. A local clone of the PowerToys repository
|
||||
1. Enable long paths in Windows (see [Enable Long Paths](https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation#enabling-long-paths-in-windows-10-version-1607-and-later) for details)
|
||||
|
||||
### Automated Setup (Recommended)
|
||||
|
||||
Run the setup script to automatically configure your development environment:
|
||||
|
||||
```powershell
|
||||
.\tools\build\setup-dev-environment.ps1
|
||||
@@ -38,10 +98,15 @@ This script will:
|
||||
- Guide you through installing required Visual Studio components from `.vsconfig`
|
||||
- Initialize git submodules
|
||||
|
||||
Run with `-Help` to see all available options.
|
||||
Run with `-Help` to see all available options:
|
||||
|
||||
<details>
|
||||
<summary><strong>Manual setup (if you prefer not to use the script)</strong></summary>
|
||||
```powershell
|
||||
.\tools\build\setup-dev-environment.ps1 -Help
|
||||
```
|
||||
|
||||
### Manual Setup
|
||||
|
||||
If you prefer to set up manually, follow these steps:
|
||||
|
||||
#### Install Visual Studio dependencies
|
||||
|
||||
@@ -50,17 +115,15 @@ Run with `-Help` to see all available options.
|
||||
|
||||
Alternatively, import the `.vsconfig` file from the repository root using Visual Studio Installer to install all required workloads.
|
||||
|
||||
#### Initialize submodules
|
||||
#### Get Submodules to compile
|
||||
|
||||
This is a one-time step required before you can compile most parts of PowerToys.
|
||||
We have submodules that need to be initialized before you can compile most parts of PowerToys. This should be a one-time step.
|
||||
|
||||
1. Open a terminal
|
||||
1. Navigate to the folder you cloned PowerToys to.
|
||||
1. Run `git submodule update --init --recursive`
|
||||
|
||||
</details>
|
||||
|
||||
### Building
|
||||
### Compiling Source Code
|
||||
|
||||
#### Using Visual Studio
|
||||
|
||||
@@ -88,77 +151,7 @@ You can also build from the command line using the provided scripts in `tools\bu
|
||||
.\tools\build\build-installer.ps1
|
||||
```
|
||||
|
||||
### Debugging
|
||||
|
||||
See [Debugging](development/debugging.md) for detailed debugging techniques, including Visual Studio setup, attaching to child processes, and troubleshooting build errors.
|
||||
|
||||
### Creating a New PowerToy
|
||||
|
||||
See [Creating a New PowerToy](development/new-powertoy.md) for an end-to-end guide covering module architecture, settings integration, installer packaging, and testing.
|
||||
|
||||
## Development Guidelines
|
||||
|
||||
- [Coding Guidelines](development/guidelines.md) - Development guidelines and best practices
|
||||
- [Coding Style](development/style.md) - Code formatting and style conventions
|
||||
- [Logging and Telemetry](development/logging.md) - How to use logging and telemetry
|
||||
- [Localization](development/localization.md) - How to support multiple languages
|
||||
- [UI Testing](development/ui-tests.md) - How to write UI tests for PowerToys
|
||||
- [Developing with VS Code](development/dev-with-vscode.md) - Build, debug, and contribute using VS Code
|
||||
|
||||
## Rules
|
||||
|
||||
- **Follow the pattern of what you already see in the code.**
|
||||
- [Coding style](development/style.md).
|
||||
- Try to package new functionality/components into libraries that have nicely defined interfaces.
|
||||
- Package new functionality into classes or refactor existing functionality into a class as you extend the code.
|
||||
- When adding new classes/methods/changing existing code, add new unit tests or update the existing tests.
|
||||
|
||||
## GitHub Workflow
|
||||
|
||||
- Before starting to work on a fix/feature, make sure there is an open issue to track the work.
|
||||
- Add the `In progress` label to the issue, if not already present. Also add a `Cost-Small/Medium/Large` estimate and make sure all appropriate labels are set.
|
||||
- If you are a community contributor, you will not be able to add labels to the issue; in that case just add a comment saying that you have started work on the issue and try to give an estimate for the delivery date.
|
||||
- If the work item has a medium/large cost, using the markdown task list, list each sub item and update the list with a check mark after completing each sub item.
|
||||
- **Before opening a PR, ensure your changes build successfully locally and functionality tests pass.** This is especially important for AI-assisted (vibe coding) contributions—always verify AI-generated code works as intended. Exploratory PRs or draft PRs for discussion are exceptions.
|
||||
- When opening a PR, follow the PR template.
|
||||
- When you'd like the team to take a look (even if the work is not yet fully complete) mark the PR as 'Ready For Review' so that the team can review your work and provide comments, suggestions, and request changes. It may take several cycles, but the end result will be solid, testable, conformant code that is safe for us to merge.
|
||||
- When the PR is approved, let the owner of the PR merge it. For community contributions, the reviewer who approved the PR can also merge it.
|
||||
- Use the `Squash and merge` option to merge a PR. If you don't want to squash it because there are logically different commits, use `Rebase and merge`.
|
||||
- Close issues automatically when referenced in a PR. You can use [closing keywords](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) in the body of the PR to have GitHub automatically link your PR to the issue.
|
||||
|
||||
## Core Architecture
|
||||
|
||||
- [Architecture Overview](core/architecture.md) - Overview of the PowerToys architecture and module interface
|
||||
- [Runner and System tray](core/runner.md) - Details about the PowerToys Runner process
|
||||
- [Settings](core/settings/readme.md) - Documentation on the settings system
|
||||
- [Installer](core/installer.md) - Information about the PowerToys installer
|
||||
- [Modules](modules/readme.md) - Documentation for individual PowerToys modules
|
||||
|
||||
## Common Components
|
||||
|
||||
- [Context Menu Handlers](common/context-menus.md) - How PowerToys implements and registers Explorer context menu handlers
|
||||
- [Monaco Editor](common/monaco-editor.md) - How PowerToys uses the Monaco code editor component across modules
|
||||
|
||||
## Tools
|
||||
|
||||
- [Tools Overview](tools/readme.md) - Overview of tools in PowerToys
|
||||
- [Build Tools](tools/build-tools.md) - Tools that help building PowerToys
|
||||
- [Bug Report Tool](tools/bug-report-tool.md) - Tool for collecting logs and system information
|
||||
- [Debugging Tools](tools/debugging-tools.md) - Specialized tools for debugging
|
||||
- [Fuzzing Testing](tools/fuzzingtesting.md) - How to implement and run fuzz testing for PowerToys modules
|
||||
|
||||
## Processes
|
||||
|
||||
- [Release Process](processes/release-process.md) - How PowerToys releases are prepared and published
|
||||
- [Update Process](processes/update-process.md) - How PowerToys updates work
|
||||
- [GPO Implementation](processes/gpo.md) - Group Policy Objects implementation details
|
||||
|
||||
## Other Resources
|
||||
|
||||
- [aka.ms links](akaLinks.md) - List of short links
|
||||
- [Issue/PR commands](commands.md) - Special commands for managing issues and pull requests
|
||||
|
||||
## Building the Installer
|
||||
## Compile the installer
|
||||
|
||||
Our installer is two parts, an EXE and an MSI. The EXE (Bootstrapper) contains the MSI and handles more complex installation logic.
|
||||
- The EXE installs all prerequisites and installs PowerToys via the MSI. It has additional features such as the installation flags (see below).
|
||||
@@ -172,3 +165,8 @@ The installer can only be compiled in `Release` mode; steps 1 and 2 must be perf
|
||||
1. Compile `PowerToysSetup.slnx` Path from root: `installer\PowerToysSetup.slnx` (details listed below)
|
||||
|
||||
See [Installer](core/installer.md) for more details on building and debugging the installer.
|
||||
|
||||
## How to create new PowerToys
|
||||
|
||||
See the instructions on [how to install the PowerToys Module project template](/tools/project_template). <br />
|
||||
Specifications for the [PowerToys settings API](core/settings/readme.md).
|
||||
|
||||
BIN
doc/images/overview/CmdPal_Hero.gif
Normal file
BIN
doc/images/overview/CmdPal_Hero.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.5 MiB |
BIN
doc/images/runner/tray.png
Normal file
BIN
doc/images/runner/tray.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
@@ -88,7 +88,7 @@
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>inc;..\..\src\;..\..\src\common\Telemetry;telemetry;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalOptions>/await /Zc:twoPhase- /Wv:18 %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalOptions>/Zc:twoPhase- /Wv:18 %(AdditionalOptions)</AdditionalOptions>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
|
||||
@@ -57,7 +57,7 @@ std::optional<fs::path> ObtainInstaller(bool& isUpToDate)
|
||||
|
||||
auto state = UpdateState::read();
|
||||
|
||||
const auto new_version_info = get_github_version_info_async().get();
|
||||
const auto new_version_info = std::move(get_github_version_info_async()).get();
|
||||
if (std::holds_alternative<version_up_to_date>(*new_version_info))
|
||||
{
|
||||
isUpToDate = true;
|
||||
@@ -76,7 +76,7 @@ std::optional<fs::path> ObtainInstaller(bool& isUpToDate)
|
||||
// Cleanup old updates before downloading the latest
|
||||
updating::cleanup_updates();
|
||||
|
||||
auto downloaded_installer = download_new_version(std::get<new_version_download_info>(*new_version_info)).get();
|
||||
auto downloaded_installer = std::move(download_new_version_async(std::get<new_version_download_info>(*new_version_info))).get();
|
||||
if (!downloaded_installer)
|
||||
{
|
||||
Logger::error("Couldn't download new installer");
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace // Strings in this namespace should not be localized
|
||||
|
||||
namespace updating
|
||||
{
|
||||
std::future<bool> uninstall_previous_msix_version_async()
|
||||
winrt::Windows::Foundation::IAsyncOperation<bool> uninstall_previous_msix_version_async()
|
||||
{
|
||||
winrt::Windows::Management::Deployment::PackageManager package_manager;
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <future>
|
||||
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <common/version/helper.h>
|
||||
|
||||
namespace updating
|
||||
{
|
||||
std::future<bool> uninstall_previous_msix_version_async();
|
||||
}
|
||||
winrt::Windows::Foundation::IAsyncOperation<bool> uninstall_previous_msix_version_async();
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <winrt/Windows.System.h>
|
||||
|
||||
#include <wil/resource.h>
|
||||
#include <wil/coroutine.h>
|
||||
|
||||
#endif //PCH_H
|
||||
|
||||
|
||||
@@ -82,11 +82,7 @@ namespace updating
|
||||
// prevent the warning that may show up depend on the value of the constants (#defines)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4702)
|
||||
#if USE_STD_EXPECTED
|
||||
std::future<std::expected<github_version_info, std::wstring>> get_github_version_info_async(const bool prerelease)
|
||||
#else
|
||||
std::future<nonstd::expected<github_version_info, std::wstring>> get_github_version_info_async(const bool prerelease)
|
||||
#endif
|
||||
wil::task<github_version_result> get_github_version_info_async(const bool prerelease)
|
||||
{
|
||||
// If the current version starts with 0.0.*, it means we're on a local build from a farm and shouldn't check for updates.
|
||||
if constexpr (VERSION_MAJOR == 0 && VERSION_MINOR == 0)
|
||||
@@ -170,7 +166,7 @@ namespace updating
|
||||
return !ec ? std::optional{ installer_download_path } : std::nullopt;
|
||||
}
|
||||
|
||||
std::future<std::optional<std::filesystem::path>> download_new_version(const new_version_download_info& new_version)
|
||||
wil::task<std::optional<std::filesystem::path>> download_new_version_async(new_version_download_info new_version)
|
||||
{
|
||||
auto installer_download_path = create_download_path();
|
||||
if (!installer_download_path)
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <future>
|
||||
#include <filesystem>
|
||||
#include <variant>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
@@ -16,6 +15,7 @@
|
||||
#endif
|
||||
|
||||
#include <common/version/helper.h>
|
||||
#include <wil/coroutine.h>
|
||||
|
||||
namespace updating
|
||||
{
|
||||
@@ -32,13 +32,15 @@ namespace updating
|
||||
};
|
||||
using github_version_info = std::variant<new_version_download_info, version_up_to_date>;
|
||||
|
||||
std::future<std::optional<std::filesystem::path>> download_new_version(const new_version_download_info& new_version);
|
||||
std::filesystem::path get_pending_updates_path();
|
||||
#if USE_STD_EXPECTED
|
||||
std::future<std::expected<github_version_info, std::wstring>> get_github_version_info_async(const bool prerelease = false);
|
||||
using github_version_result = std::expected<github_version_info, std::wstring>;
|
||||
#else
|
||||
std::future<nonstd::expected<github_version_info, std::wstring>> get_github_version_info_async(const bool prerelease = false);
|
||||
using github_version_result = nonstd::expected<github_version_info, std::wstring>;
|
||||
#endif
|
||||
|
||||
wil::task<github_version_result> get_github_version_info_async(bool prerelease = false);
|
||||
wil::task<std::optional<std::filesystem::path>> download_new_version_async(new_version_download_info new_version);
|
||||
std::filesystem::path get_pending_updates_path();
|
||||
void cleanup_updates();
|
||||
|
||||
// non-localized
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <future>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Storage.Streams.h>
|
||||
#include <winrt/Windows.Web.Http.h>
|
||||
@@ -21,15 +22,15 @@ namespace http
|
||||
headers.UserAgent().TryParseAdd(USER_AGENT);
|
||||
}
|
||||
|
||||
std::future<std::wstring> request(const winrt::Windows::Foundation::Uri& url)
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> request(winrt::Windows::Foundation::Uri url)
|
||||
{
|
||||
auto response = co_await m_client.GetAsync(url);
|
||||
(void)response.EnsureSuccessStatusCode();
|
||||
auto body = co_await response.Content().ReadAsStringAsync();
|
||||
co_return std::wstring(body);
|
||||
co_return body;
|
||||
}
|
||||
|
||||
std::future<void> download(const winrt::Windows::Foundation::Uri& url, const std::wstring& dstFilePath)
|
||||
winrt::Windows::Foundation::IAsyncAction download(winrt::Windows::Foundation::Uri url, std::wstring dstFilePath)
|
||||
{
|
||||
auto response = co_await m_client.GetAsync(url);
|
||||
(void)response.EnsureSuccessStatusCode();
|
||||
@@ -38,7 +39,7 @@ namespace http
|
||||
file_stream.Close();
|
||||
}
|
||||
|
||||
std::future<void> download(const winrt::Windows::Foundation::Uri& url, const std::wstring& dstFilePath, const std::function<void(float)>& progressUpdateCallback)
|
||||
winrt::Windows::Foundation::IAsyncAction download(winrt::Windows::Foundation::Uri url, std::wstring dstFilePath, std::function<void(float)> progressUpdateCallback)
|
||||
{
|
||||
auto response = co_await m_client.GetAsync(url, HttpCompletionOption::ResponseHeadersRead);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<AdditionalOptions>/await %(AdditionalOptions)</AdditionalOptions>
|
||||
<PreprocessorDefinitions>_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<AdditionalOptions>/await %(AdditionalOptions)</AdditionalOptions>
|
||||
<PreprocessorDefinitions>_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<AdditionalOptions>/await %(AdditionalOptions)</AdditionalOptions>
|
||||
<PreprocessorDefinitions>_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
|
||||
@@ -5140,7 +5140,7 @@ bool IsPenInverted( WPARAM wParam )
|
||||
// Captures the specified screen using the capture APIs
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
std::future<winrt::com_ptr<ID3D11Texture2D>> CaptureScreenshotAsync(winrt::IDirect3DDevice const& device, winrt::GraphicsCaptureItem const& item, winrt::DirectXPixelFormat const& pixelFormat)
|
||||
wil::task<winrt::com_ptr<ID3D11Texture2D>> CaptureScreenshotAsync(winrt::IDirect3DDevice const& device, winrt::GraphicsCaptureItem const& item, winrt::DirectXPixelFormat const& pixelFormat)
|
||||
{
|
||||
auto d3dDevice = GetDXGIInterfaceFromObject<ID3D11Device>(device);
|
||||
winrt::com_ptr<ID3D11DeviceContext> d3dContext;
|
||||
@@ -5176,9 +5176,7 @@ std::future<winrt::com_ptr<ID3D11Texture2D>> CaptureScreenshotAsync(winrt::IDire
|
||||
framePool.Close();
|
||||
|
||||
auto texture = GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame.Surface());
|
||||
auto result = util::CopyD3DTexture(d3dDevice, texture, true);
|
||||
|
||||
co_return result;
|
||||
co_return util::CopyD3DTexture(d3dDevice, texture, true);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -5205,10 +5203,7 @@ winrt::com_ptr<ID3D11Texture2D>CaptureScreenshot(winrt::DirectXPixelFormat const
|
||||
|
||||
auto item = util::CreateCaptureItemForMonitor(hMon);
|
||||
|
||||
auto capture = CaptureScreenshotAsync(device, item, pixelFormat);
|
||||
capture.wait();
|
||||
|
||||
return capture.get();
|
||||
return CaptureScreenshotAsync(device, item, pixelFormat).get();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
// WIL
|
||||
#include <wil/com.h>
|
||||
#include <wil/resource.h>
|
||||
#include <wil/coroutine.h>
|
||||
|
||||
// DirectX
|
||||
#include <d3d11_4.h>
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
"src\\common\\version\\version.vcxproj",
|
||||
"src\\modules\\cmdpal\\CmdPalKeyboardService\\CmdPalKeyboardService.vcxproj",
|
||||
"src\\modules\\cmdpal\\CmdPalModuleInterface\\CmdPalModuleInterface.vcxproj",
|
||||
"src\\modules\\cmdpal\\Core\\Microsoft.CmdPal.Core.Common\\Microsoft.CmdPal.Core.Common.csproj",
|
||||
"src\\modules\\cmdpal\\Core\\Microsoft.CmdPal.Core.ViewModels\\Microsoft.CmdPal.Core.ViewModels.csproj",
|
||||
"src\\modules\\cmdpal\\Microsoft.CmdPal.Common\\Microsoft.CmdPal.Common.csproj",
|
||||
"src\\modules\\cmdpal\\Microsoft.CmdPal.UI.ViewModels\\Microsoft.CmdPal.UI.ViewModels.csproj",
|
||||
"src\\modules\\cmdpal\\Microsoft.CmdPal.UI\\Microsoft.CmdPal.UI.csproj",
|
||||
"src\\modules\\cmdpal\\Microsoft.Terminal.UI\\Microsoft.Terminal.UI.vcxproj",
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.CmdPal.Core.Common;
|
||||
|
||||
public static class CoreLogger
|
||||
{
|
||||
public static void InitializeLogger(ILogger implementation)
|
||||
{
|
||||
_logger = implementation;
|
||||
}
|
||||
|
||||
private static ILogger? _logger;
|
||||
|
||||
public static void LogError(string message, Exception ex, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
|
||||
{
|
||||
_logger?.LogError(message, ex, memberName, sourceFilePath, sourceLineNumber);
|
||||
}
|
||||
|
||||
public static void LogError(string message, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
|
||||
{
|
||||
_logger?.LogError(message, memberName, sourceFilePath, sourceLineNumber);
|
||||
}
|
||||
|
||||
public static void LogWarning(string message, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
|
||||
{
|
||||
_logger?.LogWarning(message, memberName, sourceFilePath, sourceLineNumber);
|
||||
}
|
||||
|
||||
public static void LogInfo(string message, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
|
||||
{
|
||||
_logger?.LogInfo(message, memberName, sourceFilePath, sourceLineNumber);
|
||||
}
|
||||
|
||||
public static void LogDebug(string message, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
|
||||
{
|
||||
_logger?.LogDebug(message, memberName, sourceFilePath, sourceLineNumber);
|
||||
}
|
||||
|
||||
public static void LogTrace([System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
|
||||
{
|
||||
_logger?.LogTrace(memberName, sourceFilePath, sourceLineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
public interface ILogger
|
||||
{
|
||||
void LogError(string message, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0);
|
||||
|
||||
void LogError(string message, Exception ex, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0);
|
||||
|
||||
void LogWarning(string message, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0);
|
||||
|
||||
void LogInfo(string message, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0);
|
||||
|
||||
void LogDebug(string message, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0);
|
||||
|
||||
void LogTrace([System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0);
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\CoreCommonProps.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Microsoft.CmdPal.Core.Common</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CmdPal.Core.Common.Services;
|
||||
|
||||
public interface IExtensionService
|
||||
{
|
||||
Task<IEnumerable<IExtensionWrapper>> GetInstalledExtensionsAsync(bool includeDisabledExtensions = false);
|
||||
|
||||
// Task<IEnumerable<string>> GetInstalledHomeWidgetPackageFamilyNamesAsync(bool includeDisabledExtensions = false);
|
||||
Task<IEnumerable<IExtensionWrapper>> GetInstalledExtensionsAsync(Microsoft.CommandPalette.Extensions.ProviderType providerType, bool includeDisabledExtensions = false);
|
||||
|
||||
IExtensionWrapper? GetInstalledExtension(string extensionUniqueId);
|
||||
|
||||
Task SignalStopExtensionsAsync();
|
||||
|
||||
event TypedEventHandler<IExtensionService, IEnumerable<IExtensionWrapper>>? OnExtensionAdded;
|
||||
|
||||
event TypedEventHandler<IExtensionService, IEnumerable<IExtensionWrapper>>? OnExtensionRemoved;
|
||||
|
||||
void EnableExtension(string extensionUniqueId);
|
||||
|
||||
void DisableExtension(string extensionUniqueId);
|
||||
|
||||
///// <summary>
|
||||
///// Gets a boolean indicating whether the extension was disabled due to the corresponding Windows optional feature
|
||||
///// being absent from the machine or in an unknown state.
|
||||
///// </summary>
|
||||
///// <param name="extension">The out of proc extension object</param>
|
||||
///// <returns>True only if the extension was disabled. False otherwise.</returns>
|
||||
// public Task<bool> DisableExtensionIfWindowsFeatureNotAvailable(IExtensionWrapper extension);
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.CmdPal.Core.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// Encapsulates a navigation request within Command Palette view models.
|
||||
/// </summary>
|
||||
/// <param name="TargetViewModel">A view model that should be navigated to.</param>
|
||||
/// <param name="NavigationToken"> A <see cref="CancellationToken"/> that can be used to cancel the pending navigation.</param>
|
||||
public record AsyncNavigationRequest(object? TargetViewModel, CancellationToken NavigationToken);
|
||||
@@ -1,25 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\CoreCommonProps.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<EnableCoreMrtTooling>false</EnableCoreMrtTooling>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Common" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.CmdPal.Core.Common\Microsoft.CmdPal.Core.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,72 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.CmdPal.Core.ViewModels.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.CmdPal.Core.ViewModels.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Show details.
|
||||
/// </summary>
|
||||
public static string ShowDetailsCommand {
|
||||
get {
|
||||
return ResourceManager.GetString("ShowDetailsCommand", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ShowDetailsCommand" xml:space="preserve">
|
||||
<value>Show details</value>
|
||||
<comment>Name for the command that shows details of an item</comment>
|
||||
</data>
|
||||
</root>
|
||||
167
src/modules/cmdpal/Microsoft.CmdPal.Common/CmdPalLogger.cs
Normal file
167
src/modules/cmdpal/Microsoft.CmdPal.Common/CmdPalLogger.cs
Normal file
@@ -0,0 +1,167 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using ManagedCommon;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.CmdPal.Common;
|
||||
|
||||
// Adapter implementing Microsoft.Extensions.Logging.ILogger,
|
||||
// delegating to ManagedCommon.Logger.
|
||||
public sealed partial class CmdPalLogger : ILogger
|
||||
{
|
||||
private static readonly AsyncLocal<Stack<object>> _scopeStack = new();
|
||||
private readonly LogLevel _minLevel;
|
||||
|
||||
public string CurrentVersionLogDirectoryPath => Logger.CurrentVersionLogDirectoryPath;
|
||||
|
||||
public CmdPalLogger(LogLevel minLevel = LogLevel.Information)
|
||||
{
|
||||
_minLevel = minLevel;
|
||||
|
||||
// Ensure underlying logger initialized (idempotent if already done elsewhere).
|
||||
Logger.InitializeLogger("\\CmdPal\\Logs\\");
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None && logLevel >= _minLevel;
|
||||
|
||||
public IDisposable? BeginScope<TState>(TState state)
|
||||
where TState : notnull
|
||||
{
|
||||
var stack = _scopeStack.Value;
|
||||
if (stack is null)
|
||||
{
|
||||
stack = new Stack<object>();
|
||||
_scopeStack.Value = stack;
|
||||
}
|
||||
|
||||
stack.Push(state);
|
||||
return new Scope(stack);
|
||||
}
|
||||
|
||||
public void Log<TState>(
|
||||
LogLevel logLevel,
|
||||
EventId eventId,
|
||||
TState state,
|
||||
Exception? exception,
|
||||
Func<TState, Exception?, string> formatter)
|
||||
{
|
||||
if (!IsEnabled(logLevel))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ArgumentNullException.ThrowIfNull(formatter);
|
||||
var message = formatter(state, exception);
|
||||
if (string.IsNullOrEmpty(message) && exception is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var scopeSuffix = BuildScopeSuffix();
|
||||
var eventPrefix = eventId.Id != 0 ? $"[{eventId.Id}/{eventId.Name}] " : string.Empty;
|
||||
var finalMessage = $"{eventPrefix}{message}{scopeSuffix}";
|
||||
|
||||
switch (logLevel)
|
||||
{
|
||||
case LogLevel.Trace:
|
||||
// Existing stack: Trace logs an empty line; append message via Debug.
|
||||
Logger.LogTrace();
|
||||
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
{
|
||||
Logger.LogDebug(finalMessage);
|
||||
}
|
||||
|
||||
if (exception is not null)
|
||||
{
|
||||
Logger.LogError(exception.Message, exception);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case LogLevel.Debug:
|
||||
Logger.LogDebug(finalMessage);
|
||||
|
||||
if (exception is not null)
|
||||
{
|
||||
Logger.LogError(exception.Message, exception);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case LogLevel.Information:
|
||||
Logger.LogInfo(finalMessage);
|
||||
|
||||
if (exception is not null)
|
||||
{
|
||||
Logger.LogError(exception.Message, exception);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case LogLevel.Warning:
|
||||
Logger.LogWarning(finalMessage);
|
||||
|
||||
if (exception is not null)
|
||||
{
|
||||
Logger.LogError(exception.Message, exception);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case LogLevel.Error:
|
||||
case LogLevel.Critical:
|
||||
if (exception is not null)
|
||||
{
|
||||
Logger.LogError(finalMessage, exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogError(finalMessage);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case LogLevel.None:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static string BuildScopeSuffix()
|
||||
{
|
||||
var stack = _scopeStack.Value;
|
||||
if (stack is null || stack.Count == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// Show most-recent first.
|
||||
return $" [Scopes: {string.Join(" => ", stack.ToArray())}]";
|
||||
}
|
||||
|
||||
private sealed partial class Scope : IDisposable
|
||||
{
|
||||
private readonly Stack<object> _stack;
|
||||
private bool _disposed;
|
||||
|
||||
public Scope(Stack<object> stack) => _stack = stack;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_stack.Count > 0)
|
||||
{
|
||||
_stack.Pop();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.CmdPal.Core.Common.Helpers;
|
||||
namespace Microsoft.CmdPal.Common.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Provides utility methods for building diagnostic and error messages.
|
||||
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace Microsoft.CmdPal.Core.Common;
|
||||
namespace Microsoft.CmdPal.Common;
|
||||
|
||||
public partial class ExtensionHostInstance
|
||||
{
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.CmdPal.Core.Common.Helpers;
|
||||
namespace Microsoft.CmdPal.Common.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Thread-safe boolean implementation using atomic operations
|
||||
@@ -7,7 +7,7 @@ using System.Threading;
|
||||
|
||||
using Microsoft.UI.Dispatching;
|
||||
|
||||
namespace Microsoft.CmdPal.Core.Common.Helpers;
|
||||
namespace Microsoft.CmdPal.Common.Helpers;
|
||||
|
||||
public static partial class NativeEventWaiter
|
||||
{
|
||||
@@ -6,7 +6,7 @@ using System.Runtime.CompilerServices;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Storage.FileSystem;
|
||||
|
||||
namespace Microsoft.CmdPal.Core.Common.Helpers;
|
||||
namespace Microsoft.CmdPal.Common.Helpers;
|
||||
|
||||
public static class PathHelper
|
||||
{
|
||||
@@ -6,7 +6,7 @@ using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.CmdPal.Core.Common.Helpers;
|
||||
namespace Microsoft.CmdPal.Common.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// An async gate that ensures only one operation runs at a time.
|
||||
@@ -2,7 +2,7 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.CmdPal.Core.Common.Helpers;
|
||||
namespace Microsoft.CmdPal.Common.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// An async gate that ensures only one value computation runs at a time.
|
||||
@@ -3,9 +3,10 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Windows.ApplicationModel;
|
||||
|
||||
namespace Microsoft.CmdPal.Core.Common.Helpers;
|
||||
namespace Microsoft.CmdPal.Common.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Helper class for retrieving application version information safely.
|
||||
@@ -37,7 +38,7 @@ internal static class VersionHelper
|
||||
/// </summary>
|
||||
/// <param name="version">The version string if successful, or an empty string if unsuccessful.</param>
|
||||
/// <returns>True if the version was retrieved successfully; otherwise, false.</returns>
|
||||
private static bool TryGetPackagedVersion(out string version)
|
||||
private static bool TryGetPackagedVersion(out string version, ILogger logger)
|
||||
{
|
||||
version = string.Empty;
|
||||
try
|
||||
@@ -53,7 +54,7 @@ internal static class VersionHelper
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CoreLogger.LogError("Failed to get version from the package", ex);
|
||||
Log_FailedToGetVersion(logger, ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -63,7 +64,7 @@ internal static class VersionHelper
|
||||
/// </summary>
|
||||
/// <param name="version">The version string if successful, or an empty string if unsuccessful.</param>
|
||||
/// <returns>True if the version was retrieved successfully; otherwise, false.</returns>
|
||||
private static bool TryGetAssemblyVersion(out string version)
|
||||
private static bool TryGetAssemblyVersion(out string version, ILogger logger)
|
||||
{
|
||||
version = string.Empty;
|
||||
try
|
||||
@@ -80,8 +81,14 @@ internal static class VersionHelper
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CoreLogger.LogError("Failed to get version from the executable", ex);
|
||||
Log_FailedToGetVersionFromExe(logger, ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[LoggerMessage(Level = LogLevel.Error, Message = "Failed to get version from the package")]
|
||||
static partial void Log_FailedToGetVersion(ILogger logger, Exception ex);
|
||||
|
||||
[LoggerMessage(Level = LogLevel.Error, Message = "Failed to get version from the executable")]
|
||||
static partial void Log_FailedToGetVersionFromExe(ILogger logger, Exception ex);
|
||||
}
|
||||
@@ -7,7 +7,7 @@ using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
using Windows.System;
|
||||
|
||||
namespace Microsoft.CmdPal.Core.Common.Helpers;
|
||||
namespace Microsoft.CmdPal.Common.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Well-known key chords used in the Command Palette and extensions.
|
||||
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\CoreCommonProps.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<RootNamespace>Microsoft.CmdPal.Common</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
164
src/modules/cmdpal/Microsoft.CmdPal.Common/PersistenceService.cs
Normal file
164
src/modules/cmdpal/Microsoft.CmdPal.Common/PersistenceService.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.CmdPal.Common;
|
||||
|
||||
public partial class PersistenceService
|
||||
{
|
||||
private static bool TryParseJsonObject(string json, ILogger logger, [NotNullWhen(true)] out JsonObject? obj)
|
||||
{
|
||||
obj = null;
|
||||
try
|
||||
{
|
||||
obj = JsonNode.Parse(json) as JsonObject;
|
||||
return obj is not null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log_PersistenceParseFailure(logger, ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryReadSavedObject(string filePath, ILogger logger, [NotNullWhen(true)] out JsonObject? saved)
|
||||
{
|
||||
saved = null;
|
||||
|
||||
string oldContent;
|
||||
try
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
saved = new JsonObject();
|
||||
return true;
|
||||
}
|
||||
|
||||
oldContent = File.ReadAllText(filePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log_PersistenceReadFileFailure(logger, filePath, ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(oldContent))
|
||||
{
|
||||
Log_FileEmpty(logger, filePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
return TryParseJsonObject(oldContent, logger, out saved);
|
||||
}
|
||||
|
||||
public static T LoadObject<T>(string filePath, JsonTypeInfo<T> typeInfo, ILogger logger)
|
||||
where T : new()
|
||||
{
|
||||
if (string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
throw new InvalidOperationException($"You must set a valid file path before loading {typeof(T).Name}");
|
||||
}
|
||||
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
Log_FileDoesntExist(logger, typeof(T).Name, filePath);
|
||||
return new T();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var jsonContent = File.ReadAllText(filePath);
|
||||
var loaded = JsonSerializer.Deserialize(jsonContent, typeInfo);
|
||||
return loaded ?? new T();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log_PersistenceReadFailure(logger, typeof(T).Name, filePath, ex);
|
||||
return new T();
|
||||
}
|
||||
}
|
||||
|
||||
public static void SaveObject<T>(
|
||||
T model,
|
||||
string filePath,
|
||||
JsonTypeInfo<T> typeInfo,
|
||||
JsonSerializerOptions optionsForWrite,
|
||||
Action<JsonObject>? beforeWriteMutation,
|
||||
Action<T>? afterWriteCallback,
|
||||
ILogger logger)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
throw new InvalidOperationException($"You must set a valid file path before saving {typeof(T).Name}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var json = JsonSerializer.Serialize(model, typeInfo);
|
||||
|
||||
if (!TryParseJsonObject(json, logger, out var newObj))
|
||||
{
|
||||
Log_SerializationError(logger, typeof(T).Name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryReadSavedObject(filePath, logger, out var savedObj))
|
||||
{
|
||||
savedObj = new JsonObject();
|
||||
}
|
||||
|
||||
foreach (var kvp in newObj)
|
||||
{
|
||||
savedObj[kvp.Key] = kvp.Value?.DeepClone();
|
||||
}
|
||||
|
||||
beforeWriteMutation?.Invoke(savedObj);
|
||||
|
||||
var serialized = savedObj.ToJsonString(optionsForWrite);
|
||||
File.WriteAllText(filePath, serialized);
|
||||
|
||||
afterWriteCallback?.Invoke(model);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log_PersistenceSaveFailure(logger, typeof(T).Name, filePath, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static string SettingsJsonPath(string fileName)
|
||||
{
|
||||
var directory = Utilities.BaseSettingsPath("Microsoft.CommandPalette");
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
// now, the settings is just next to the exe
|
||||
return Path.Combine(directory, fileName);
|
||||
}
|
||||
|
||||
[LoggerMessage(Level = LogLevel.Error, Message = "Failed to save {typeName} to '{filePath}'.")]
|
||||
static partial void Log_PersistenceSaveFailure(ILogger logger, string typeName, string filePath, Exception exception);
|
||||
|
||||
[LoggerMessage(Level = LogLevel.Error, Message = "Failed to read {typeName} from '{filePath}'.")]
|
||||
static partial void Log_PersistenceReadFailure(ILogger logger, string typeName, string filePath, Exception exception);
|
||||
|
||||
[LoggerMessage(Level = LogLevel.Debug, Message = "Failed to serialize {typeName} to JsonObject.")]
|
||||
static partial void Log_SerializationError(ILogger logger, string typeName);
|
||||
|
||||
[LoggerMessage(Level = LogLevel.Debug, Message = "The provided {typeName} file does not exist ({filePath})")]
|
||||
static partial void Log_FileDoesntExist(ILogger logger, string typeName, string filePath);
|
||||
|
||||
[LoggerMessage(Level = LogLevel.Debug, Message = "The file at '{filePath}' is empty.")]
|
||||
static partial void Log_FileEmpty(ILogger logger, string filePath);
|
||||
|
||||
[LoggerMessage(Level = LogLevel.Error, Message = "Failed to read file at '{filePath}'.")]
|
||||
static partial void Log_PersistenceReadFileFailure(ILogger logger, string filePath, Exception exception);
|
||||
|
||||
[LoggerMessage(Level = LogLevel.Error, Message = "Failed to parse persisted JSON.")]
|
||||
static partial void Log_PersistenceParseFailure(ILogger logger, Exception exception);
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.CmdPal.Core.Common.Properties {
|
||||
namespace Microsoft.CmdPal.Common.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Microsoft.CmdPal.Core.Common.Properties {
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
public class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
@@ -36,10 +36,10 @@ namespace Microsoft.CmdPal.Core.Common.Properties {
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
public static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.CmdPal.Core.Common.Properties.Resources", typeof(Resources).Assembly);
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.CmdPal.Common.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
@@ -51,7 +51,7 @@ namespace Microsoft.CmdPal.Core.Common.Properties {
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
public static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
@@ -67,7 +67,7 @@ namespace Microsoft.CmdPal.Core.Common.Properties {
|
||||
///
|
||||
///(While you’re at it, give the details below a quick skim — just to make sure there’s nothing personal you’d prefer not to share. It’s rare, but sometimes little surprises sneak in.).
|
||||
/// </summary>
|
||||
internal static string ErrorReport_Global_Preamble {
|
||||
public static string ErrorReport_Global_Preamble {
|
||||
get {
|
||||
return ResourceManager.GetString("ErrorReport_Global_Preamble", resourceCulture);
|
||||
}
|
||||
@@ -5,8 +5,8 @@
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Principal;
|
||||
using Microsoft.CmdPal.Core.Common.Helpers;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Windows.ApplicationModel;
|
||||
|
||||
namespace Microsoft.CmdPal.Core.Common.Services;
|
||||
@@ -14,20 +14,22 @@ namespace Microsoft.CmdPal.Core.Common.Services;
|
||||
/// <summary>
|
||||
/// Implementation of IApplicationInfoService providing application-wide information.
|
||||
/// </summary>
|
||||
public sealed class ApplicationInfoService : IApplicationInfoService
|
||||
public sealed partial class ApplicationInfoService : IApplicationInfoService
|
||||
{
|
||||
private readonly Lazy<string> _configDirectory = new(() => Utilities.BaseSettingsPath("Microsoft.CmdPal"));
|
||||
private readonly Lazy<bool> _isElevated;
|
||||
private readonly Lazy<string> _logDirectory;
|
||||
private readonly Lazy<AppPackagingFlavor> _packagingFlavor;
|
||||
private readonly ILogger _logger;
|
||||
private Func<string>? _getLogDirectory;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ApplicationInfoService"/> class.
|
||||
/// The log directory delegate can be set later via <see cref="SetLogDirectory(Func{string})"/>.
|
||||
/// </summary>
|
||||
public ApplicationInfoService()
|
||||
public ApplicationInfoService(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_packagingFlavor = new Lazy<AppPackagingFlavor>(DeterminePackagingFlavor);
|
||||
_isElevated = new Lazy<bool>(DetermineElevationStatus);
|
||||
_logDirectory = new Lazy<string>(() => _getLogDirectory?.Invoke() ?? "Not available");
|
||||
@@ -37,10 +39,13 @@ public sealed class ApplicationInfoService : IApplicationInfoService
|
||||
/// Initializes a new instance of the <see cref="ApplicationInfoService"/> class with an optional log directory provider.
|
||||
/// </summary>
|
||||
/// <param name="getLogDirectory">Optional delegate to retrieve the log directory path. If not provided, the log directory will be unavailable.</param>
|
||||
public ApplicationInfoService(Func<string>? getLogDirectory)
|
||||
: this()
|
||||
public ApplicationInfoService(
|
||||
Func<string>? getLogDirectory,
|
||||
ILogger logger)
|
||||
: this(logger)
|
||||
{
|
||||
_getLogDirectory = getLogDirectory;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -87,7 +92,7 @@ public sealed class ApplicationInfoService : IApplicationInfoService
|
||||
""";
|
||||
}
|
||||
|
||||
private static AppPackagingFlavor DeterminePackagingFlavor()
|
||||
private AppPackagingFlavor DeterminePackagingFlavor()
|
||||
{
|
||||
// Try to determine if running as packaged
|
||||
try
|
||||
@@ -105,7 +110,7 @@ public sealed class ApplicationInfoService : IApplicationInfoService
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CoreLogger.LogError("Failed to determine packaging flavor", ex);
|
||||
Log_FailedToDeterminePackagingFlavor(ex);
|
||||
return AppPackagingFlavor.Unpackaged;
|
||||
}
|
||||
}
|
||||
@@ -122,4 +127,7 @@ public sealed class ApplicationInfoService : IApplicationInfoService
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[LoggerMessage(Level = LogLevel.Error, Message = "Failed to determine packaging flavor")]
|
||||
partial void Log_FailedToDeterminePackagingFlavor(Exception ex);
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.CmdPal.Core.Common.Services;
|
||||
namespace Microsoft.CmdPal.Common.Services;
|
||||
|
||||
public interface IRunHistoryService
|
||||
{
|
||||
@@ -3,45 +3,44 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public partial class AliasManager : ObservableObject
|
||||
{
|
||||
private readonly TopLevelCommandManager _topLevelCommandManager;
|
||||
// private readonly TopLevelCommandManager _topLevelCommandManager;
|
||||
private readonly SettingsService _settingsService;
|
||||
|
||||
// REMEMBER, CommandAlias.SearchPrefix is what we use as keys
|
||||
private readonly Dictionary<string, CommandAlias> _aliases;
|
||||
private Dictionary<string, CommandAlias> Aliases => _settingsService.CurrentSettings.Aliases;
|
||||
|
||||
public AliasManager(TopLevelCommandManager tlcManager, SettingsModel settings)
|
||||
// TopLevelCommandManager tlcManager,
|
||||
public AliasManager(SettingsService settingsService)
|
||||
{
|
||||
_topLevelCommandManager = tlcManager;
|
||||
_aliases = settings.Aliases;
|
||||
// _topLevelCommandManager = tlcManager;
|
||||
_settingsService = settingsService;
|
||||
|
||||
if (_aliases.Count == 0)
|
||||
if (Aliases.Count == 0)
|
||||
{
|
||||
PopulateDefaultAliases();
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAlias(CommandAlias a) => _aliases.Add(a.SearchPrefix, a);
|
||||
private void AddAlias(CommandAlias a) => Aliases.Add(a.SearchPrefix, a);
|
||||
|
||||
public bool CheckAlias(string searchText)
|
||||
{
|
||||
if (_aliases.TryGetValue(searchText, out var alias))
|
||||
if (Aliases.TryGetValue(searchText, out var alias))
|
||||
{
|
||||
try
|
||||
{
|
||||
var topLevelCommand = _topLevelCommandManager.LookupCommand(alias.CommandId);
|
||||
if (topLevelCommand is not null)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send<ClearSearchMessage>();
|
||||
|
||||
WeakReferenceMessenger.Default.Send<PerformCommandMessage>(topLevelCommand.GetPerformCommandMessage());
|
||||
return true;
|
||||
}
|
||||
// var topLevelCommand = _topLevelCommandManager.LookupCommand(alias.CommandId);
|
||||
// if (topLevelCommand is not null)
|
||||
// {
|
||||
// WeakReferenceMessenger.Default.Send<ClearSearchMessage>();
|
||||
// WeakReferenceMessenger.Default.Send<PerformCommandMessage>(topLevelCommand.GetPerformCommandMessage());
|
||||
// return true;
|
||||
// }
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -65,7 +64,7 @@ public partial class AliasManager : ObservableObject
|
||||
|
||||
public string? KeysFromId(string commandId)
|
||||
{
|
||||
return _aliases
|
||||
return Aliases
|
||||
.Where(kv => kv.Value.CommandId == commandId)
|
||||
.Select(kv => kv.Value.Alias)
|
||||
.FirstOrDefault();
|
||||
@@ -73,7 +72,7 @@ public partial class AliasManager : ObservableObject
|
||||
|
||||
public CommandAlias? AliasFromId(string commandId)
|
||||
{
|
||||
return _aliases
|
||||
return Aliases
|
||||
.Where(kv => kv.Value.CommandId == commandId)
|
||||
.Select(kv => kv.Value)
|
||||
.FirstOrDefault();
|
||||
@@ -89,7 +88,7 @@ public partial class AliasManager : ObservableObject
|
||||
|
||||
// If we already have _this exact alias_, do nothing
|
||||
if (newAlias is not null &&
|
||||
_aliases.TryGetValue(newAlias.SearchPrefix, out var existingAlias))
|
||||
Aliases.TryGetValue(newAlias.SearchPrefix, out var existingAlias))
|
||||
{
|
||||
if (existingAlias.CommandId == commandId)
|
||||
{
|
||||
@@ -98,7 +97,7 @@ public partial class AliasManager : ObservableObject
|
||||
}
|
||||
|
||||
List<CommandAlias> toRemove = [];
|
||||
foreach (var kv in _aliases)
|
||||
foreach (var kv in Aliases)
|
||||
{
|
||||
// Look for the old aliases for the command, and remove it
|
||||
if (kv.Value.CommandId == commandId)
|
||||
@@ -112,18 +111,18 @@ public partial class AliasManager : ObservableObject
|
||||
toRemove.Add(kv.Value);
|
||||
|
||||
// Remove alias from other TopLevelViewModels it may be assigned to
|
||||
var topLevelCommand = _topLevelCommandManager.LookupCommand(kv.Value.CommandId);
|
||||
if (topLevelCommand is not null)
|
||||
{
|
||||
topLevelCommand.AliasText = string.Empty;
|
||||
}
|
||||
// var topLevelCommand = _topLevelCommandManager.LookupCommand(kv.Value.CommandId);
|
||||
// if (topLevelCommand is not null)
|
||||
// {
|
||||
// topLevelCommand.AliasText = string.Empty;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var alias in toRemove)
|
||||
{
|
||||
// REMEMBER, SearchPrefix is what we use as keys
|
||||
_aliases.Remove(alias.SearchPrefix);
|
||||
Aliases.Remove(alias.SearchPrefix);
|
||||
}
|
||||
|
||||
if (newAlias is not null)
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.CmdPal.Core.Common;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CmdPal.Core.ViewModels;
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public abstract partial class AppExtensionHost : IExtensionHost
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private static readonly GlobalLogPageContext _globalLogPageContext = new();
|
||||
|
||||
private static ulong _hostingHwnd;
|
||||
@@ -27,6 +29,11 @@ public abstract partial class AppExtensionHost : IExtensionHost
|
||||
|
||||
public static void SetHostHwnd(ulong hostHwnd) => _hostingHwnd = hostHwnd;
|
||||
|
||||
public AppExtensionHost(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void DebugLog(string message)
|
||||
{
|
||||
#if DEBUG
|
||||
@@ -60,7 +67,7 @@ public abstract partial class AppExtensionHost : IExtensionHost
|
||||
return Task.CompletedTask.AsAsyncAction();
|
||||
}
|
||||
|
||||
CoreLogger.LogDebug(message.Message);
|
||||
Log_Message(message.Message);
|
||||
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
@@ -158,6 +165,9 @@ public abstract partial class AppExtensionHost : IExtensionHost
|
||||
}
|
||||
|
||||
public abstract string? GetExtensionDisplayName();
|
||||
|
||||
[LoggerMessage(Level = LogLevel.Debug, Message = "{message}")]
|
||||
partial void Log_Message(string message);
|
||||
}
|
||||
|
||||
public interface IAppHostService
|
||||
@@ -2,25 +2,12 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public partial class AppStateModel : ObservableObject
|
||||
{
|
||||
[JsonIgnore]
|
||||
public static readonly string FilePath;
|
||||
|
||||
public event TypedEventHandler<AppStateModel, object?>? StateChanged;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// STATE HERE
|
||||
// Make sure that you make the setters public (JsonSerializer.Deserialize will fail silently otherwise)!
|
||||
@@ -28,144 +15,4 @@ public partial class AppStateModel : ObservableObject
|
||||
public RecentCommandsManager RecentCommands { get; set; } = new();
|
||||
|
||||
public List<string> RunHistory { get; set; } = [];
|
||||
|
||||
// END SETTINGS
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static AppStateModel()
|
||||
{
|
||||
FilePath = StateJsonPath();
|
||||
}
|
||||
|
||||
public static AppStateModel LoadState()
|
||||
{
|
||||
if (string.IsNullOrEmpty(FilePath))
|
||||
{
|
||||
throw new InvalidOperationException($"You must set a valid {nameof(FilePath)} before calling {nameof(LoadState)}");
|
||||
}
|
||||
|
||||
if (!File.Exists(FilePath))
|
||||
{
|
||||
Debug.WriteLine("The provided settings file does not exist");
|
||||
return new();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Read the JSON content from the file
|
||||
var jsonContent = File.ReadAllText(FilePath);
|
||||
|
||||
var loaded = JsonSerializer.Deserialize<AppStateModel>(jsonContent, JsonSerializationContext.Default.AppStateModel);
|
||||
|
||||
Debug.WriteLine(loaded is not null ? "Loaded settings file" : "Failed to parse");
|
||||
|
||||
return loaded ?? new();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex.ToString());
|
||||
}
|
||||
|
||||
return new();
|
||||
}
|
||||
|
||||
public static void SaveState(AppStateModel model)
|
||||
{
|
||||
if (string.IsNullOrEmpty(FilePath))
|
||||
{
|
||||
throw new InvalidOperationException($"You must set a valid {nameof(FilePath)} before calling {nameof(SaveState)}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Serialize the main dictionary to JSON and save it to the file
|
||||
var settingsJson = JsonSerializer.Serialize(model, JsonSerializationContext.Default.AppStateModel!);
|
||||
|
||||
// validate JSON
|
||||
if (JsonNode.Parse(settingsJson) is not JsonObject newSettings)
|
||||
{
|
||||
Logger.LogError("Failed to parse app state as a JsonObject.");
|
||||
return;
|
||||
}
|
||||
|
||||
// read previous settings
|
||||
if (!TryReadSavedState(out var savedSettings))
|
||||
{
|
||||
savedSettings = new JsonObject();
|
||||
}
|
||||
|
||||
// merge new settings into old ones
|
||||
foreach (var item in newSettings)
|
||||
{
|
||||
savedSettings[item.Key] = item.Value?.DeepClone();
|
||||
}
|
||||
|
||||
var serialized = savedSettings.ToJsonString(JsonSerializationContext.Default.AppStateModel!.Options);
|
||||
File.WriteAllText(FilePath, serialized);
|
||||
|
||||
// TODO: Instead of just raising the event here, we should
|
||||
// have a file change watcher on the settings file, and
|
||||
// reload the settings then
|
||||
model.StateChanged?.Invoke(model, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Failed to save application state to {FilePath}:", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryReadSavedState([NotNullWhen(true)] out JsonObject? savedSettings)
|
||||
{
|
||||
savedSettings = null;
|
||||
|
||||
// read existing content from the file
|
||||
string oldContent;
|
||||
try
|
||||
{
|
||||
if (File.Exists(FilePath))
|
||||
{
|
||||
oldContent = File.ReadAllText(FilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// file doesn't exist (might not have been created yet), so consider this a success
|
||||
// and return empty settings
|
||||
savedSettings = new JsonObject();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning($"Failed to read app state file {FilePath}:\n{ex}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// detect empty file, just for sake of logging
|
||||
if (string.IsNullOrWhiteSpace(oldContent))
|
||||
{
|
||||
Logger.LogInfo($"App state file is empty: {FilePath}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// is it valid JSON?
|
||||
try
|
||||
{
|
||||
savedSettings = JsonNode.Parse(oldContent) as JsonObject;
|
||||
return savedSettings != null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning($"Failed to parse app state from {FilePath}:\n{ex}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal static string StateJsonPath()
|
||||
{
|
||||
var directory = Utilities.BaseSettingsPath("Microsoft.CmdPal");
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
// now, the settings is just next to the exe
|
||||
return Path.Combine(directory, "state.json");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.CmdPal.Common;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public partial class AppStateService
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly string _filePath;
|
||||
private AppStateModel _appStateModel;
|
||||
|
||||
public event TypedEventHandler<AppStateModel, object?>? StateChanged;
|
||||
|
||||
public AppStateModel CurrentSettings => _appStateModel;
|
||||
|
||||
public AppStateService(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_filePath = PersistenceService.SettingsJsonPath("state.json");
|
||||
_appStateModel = LoadState();
|
||||
}
|
||||
|
||||
private AppStateModel LoadState()
|
||||
{
|
||||
return PersistenceService.LoadObject<AppStateModel>(_filePath, JsonSerializationContext.Default.AppStateModel!, _logger);
|
||||
}
|
||||
|
||||
public void SaveSettings(AppStateModel model)
|
||||
{
|
||||
PersistenceService.SaveObject(
|
||||
model,
|
||||
_filePath,
|
||||
JsonSerializationContext.Default.AppStateModel,
|
||||
JsonSerializationContext.Default.Options,
|
||||
null,
|
||||
afterWriteCallback: m => FinalizeStateSave(m),
|
||||
_logger);
|
||||
}
|
||||
|
||||
private void FinalizeStateSave(AppStateModel model)
|
||||
{
|
||||
_appStateModel = model;
|
||||
StateChanged?.Invoke(model, null);
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,7 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
Color.FromArgb(255, 126, 115, 95), // #7e735f
|
||||
];
|
||||
|
||||
private readonly SettingsModel _settings;
|
||||
private readonly SettingsService _settingsService;
|
||||
private readonly UISettings _uiSettings;
|
||||
private readonly IThemeService _themeService;
|
||||
private readonly DispatcherQueueTimer _saveTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
|
||||
@@ -96,22 +96,24 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
private ElementTheme? _elementThemeOverride;
|
||||
private Color _currentSystemAccentColor;
|
||||
|
||||
private SettingsModel Settings => _settingsService.CurrentSettings;
|
||||
|
||||
public ObservableCollection<Color> Swatches => WindowsColorSwatches;
|
||||
|
||||
public int ThemeIndex
|
||||
{
|
||||
get => (int)_settings.Theme;
|
||||
get => (int)Settings.Theme;
|
||||
set => Theme = (UserTheme)value;
|
||||
}
|
||||
|
||||
public UserTheme Theme
|
||||
{
|
||||
get => _settings.Theme;
|
||||
get => Settings.Theme;
|
||||
set
|
||||
{
|
||||
if (_settings.Theme != value)
|
||||
if (Settings.Theme != value)
|
||||
{
|
||||
_settings.Theme = value;
|
||||
Settings.Theme = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(ThemeIndex));
|
||||
Save();
|
||||
@@ -121,12 +123,12 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
public ColorizationMode ColorizationMode
|
||||
{
|
||||
get => _settings.ColorizationMode;
|
||||
get => Settings.ColorizationMode;
|
||||
set
|
||||
{
|
||||
if (_settings.ColorizationMode != value)
|
||||
if (Settings.ColorizationMode != value)
|
||||
{
|
||||
_settings.ColorizationMode = value;
|
||||
Settings.ColorizationMode = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(ColorizationModeIndex));
|
||||
OnPropertyChanged(nameof(IsCustomTintVisible));
|
||||
@@ -152,18 +154,18 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
public int ColorizationModeIndex
|
||||
{
|
||||
get => (int)_settings.ColorizationMode;
|
||||
get => (int)Settings.ColorizationMode;
|
||||
set => ColorizationMode = (ColorizationMode)value;
|
||||
}
|
||||
|
||||
public Color ThemeColor
|
||||
{
|
||||
get => _settings.CustomThemeColor;
|
||||
get => Settings.CustomThemeColor;
|
||||
set
|
||||
{
|
||||
if (_settings.CustomThemeColor != value)
|
||||
if (Settings.CustomThemeColor != value)
|
||||
{
|
||||
_settings.CustomThemeColor = value;
|
||||
Settings.CustomThemeColor = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
|
||||
@@ -179,10 +181,10 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
public int ColorIntensity
|
||||
{
|
||||
get => _settings.CustomThemeColorIntensity;
|
||||
get => Settings.CustomThemeColorIntensity;
|
||||
set
|
||||
{
|
||||
_settings.CustomThemeColorIntensity = value;
|
||||
Settings.CustomThemeColorIntensity = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(EffectiveTintIntensity));
|
||||
Save();
|
||||
@@ -203,12 +205,12 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
public string BackgroundImagePath
|
||||
{
|
||||
get => _settings.BackgroundImagePath ?? string.Empty;
|
||||
get => Settings.BackgroundImagePath ?? string.Empty;
|
||||
set
|
||||
{
|
||||
if (_settings.BackgroundImagePath != value)
|
||||
if (Settings.BackgroundImagePath != value)
|
||||
{
|
||||
_settings.BackgroundImagePath = value;
|
||||
Settings.BackgroundImagePath = value;
|
||||
OnPropertyChanged();
|
||||
|
||||
if (BackgroundImageOpacity == 0)
|
||||
@@ -223,12 +225,12 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
public int BackgroundImageOpacity
|
||||
{
|
||||
get => _settings.BackgroundImageOpacity;
|
||||
get => Settings.BackgroundImageOpacity;
|
||||
set
|
||||
{
|
||||
if (_settings.BackgroundImageOpacity != value)
|
||||
if (Settings.BackgroundImageOpacity != value)
|
||||
{
|
||||
_settings.BackgroundImageOpacity = value;
|
||||
Settings.BackgroundImageOpacity = value;
|
||||
OnPropertyChanged();
|
||||
Save();
|
||||
}
|
||||
@@ -237,12 +239,12 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
public int BackgroundImageBrightness
|
||||
{
|
||||
get => _settings.BackgroundImageBrightness;
|
||||
get => Settings.BackgroundImageBrightness;
|
||||
set
|
||||
{
|
||||
if (_settings.BackgroundImageBrightness != value)
|
||||
if (Settings.BackgroundImageBrightness != value)
|
||||
{
|
||||
_settings.BackgroundImageBrightness = value;
|
||||
Settings.BackgroundImageBrightness = value;
|
||||
OnPropertyChanged();
|
||||
Save();
|
||||
}
|
||||
@@ -251,12 +253,12 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
public int BackgroundImageBlurAmount
|
||||
{
|
||||
get => _settings.BackgroundImageBlurAmount;
|
||||
get => Settings.BackgroundImageBlurAmount;
|
||||
set
|
||||
{
|
||||
if (_settings.BackgroundImageBlurAmount != value)
|
||||
if (Settings.BackgroundImageBlurAmount != value)
|
||||
{
|
||||
_settings.BackgroundImageBlurAmount = value;
|
||||
Settings.BackgroundImageBlurAmount = value;
|
||||
OnPropertyChanged();
|
||||
Save();
|
||||
}
|
||||
@@ -265,12 +267,12 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
public BackgroundImageFit BackgroundImageFit
|
||||
{
|
||||
get => _settings.BackgroundImageFit;
|
||||
get => Settings.BackgroundImageFit;
|
||||
set
|
||||
{
|
||||
if (_settings.BackgroundImageFit != value)
|
||||
if (Settings.BackgroundImageFit != value)
|
||||
{
|
||||
_settings.BackgroundImageFit = value;
|
||||
Settings.BackgroundImageFit = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(BackgroundImageFitIndex));
|
||||
Save();
|
||||
@@ -387,7 +389,7 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
[ObservableProperty]
|
||||
public partial bool IsColorizationDetailsExpanded { get; set; }
|
||||
|
||||
public bool IsCustomTintVisible => _settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.Image;
|
||||
public bool IsCustomTintVisible => Settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.Image;
|
||||
|
||||
public bool IsColorIntensityVisible => _settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.WindowsAccentColor;
|
||||
|
||||
@@ -399,12 +401,13 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
public int EffectiveTintIntensity => _settings.ColorizationMode is ColorizationMode.Image
|
||||
? _settings.BackgroundImageTintIntensity
|
||||
: _settings.CustomThemeColorIntensity;
|
||||
public bool IsCustomTintIntensityVisible => Settings.ColorizationMode is ColorizationMode.CustomColor or ColorizationMode.WindowsAccentColor or ColorizationMode.Image;
|
||||
|
||||
public bool IsBackgroundControlsVisible => _settings.ColorizationMode is ColorizationMode.Image;
|
||||
public bool IsBackgroundControlsVisible => Settings.ColorizationMode is ColorizationMode.Image;
|
||||
|
||||
public bool IsNoBackgroundVisible => _settings.ColorizationMode is ColorizationMode.None;
|
||||
public bool IsNoBackgroundVisible => Settings.ColorizationMode is ColorizationMode.None;
|
||||
|
||||
public bool IsAccentColorControlsVisible => _settings.ColorizationMode is ColorizationMode.WindowsAccentColor;
|
||||
public bool IsAccentColorControlsVisible => Settings.ColorizationMode is ColorizationMode.WindowsAccentColor;
|
||||
|
||||
public bool IsResetButtonVisible => _settings.ColorizationMode is ColorizationMode.Image;
|
||||
|
||||
@@ -436,11 +439,11 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
? new Microsoft.UI.Xaml.Media.Imaging.BitmapImage(uri)
|
||||
: null;
|
||||
|
||||
public AppearanceSettingsViewModel(IThemeService themeService, SettingsModel settings)
|
||||
public AppearanceSettingsViewModel(IThemeService themeService, SettingsService settingsService)
|
||||
{
|
||||
_themeService = themeService;
|
||||
_themeService.ThemeChanged += ThemeServiceOnThemeChanged;
|
||||
_settings = settings;
|
||||
_settingsService = settingsService;
|
||||
|
||||
_uiSettings = new UISettings();
|
||||
_uiSettings.ColorValuesChanged += UiSettingsOnColorValuesChanged;
|
||||
@@ -448,7 +451,7 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
Reapply();
|
||||
|
||||
IsColorizationDetailsExpanded = _settings.ColorizationMode != ColorizationMode.None && IsBackgroundSettingsEnabled;
|
||||
SettingsIsColorizationDetailsExpanded = _settings.ColorizationMode != ColorizationMode.None && IsBackgroundSettingsEnabled;
|
||||
}
|
||||
|
||||
private void UiSettingsOnColorValuesChanged(UISettings sender, object args) => _uiDispatcher.TryEnqueue(() => UpdateAccentColor(sender));
|
||||
@@ -469,7 +472,7 @@ public sealed partial class AppearanceSettingsViewModel : ObservableObject, IDis
|
||||
|
||||
private void Save()
|
||||
{
|
||||
SettingsModel.SaveSettings(_settings);
|
||||
_settingsService.SaveSettings(Settings);
|
||||
_saveTimer.Debounce(Reapply, TimeSpan.FromMilliseconds(200));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// Encapsulates a navigation request within Command Palette view models.
|
||||
/// </summary>
|
||||
/// <param name="TargetViewModel">A view model that should be navigated to.</param>
|
||||
/// <param name="NavigationToken"> A <see cref="CancellationToken"/> that can be used to cancel the pending navigation.</param>
|
||||
public record AsyncNavigationRequest(object? TargetViewModel, CancellationToken NavigationToken);
|
||||
|
||||
#pragma warning disable SA1402 // File may only contain a single type
|
||||
|
||||
public record AsyncListPageNavigationRequest(object? TargetViewModel, SettingsService SettingsService, ILogger Logger, CancellationToken NavigationToken)
|
||||
: AsyncNavigationRequest(TargetViewModel, NavigationToken);
|
||||
|
||||
public record AsyncContentPageNavigationRequest(object? TargetViewModel, object? ImageProvider, CancellationToken NavigationToken)
|
||||
: AsyncNavigationRequest(TargetViewModel, NavigationToken);
|
||||
|
||||
#pragma warning restore SA1402 // File may only contain a single type
|
||||
@@ -4,15 +4,18 @@
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Windows.System;
|
||||
|
||||
namespace Microsoft.CmdPal.Core.ViewModels;
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public partial class CommandBarViewModel : ObservableObject,
|
||||
IRecipient<UpdateCommandBarMessage>
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public ICommandBarContext? SelectedItem
|
||||
{
|
||||
get => field;
|
||||
@@ -48,8 +51,9 @@ public partial class CommandBarViewModel : ObservableObject,
|
||||
[ObservableProperty]
|
||||
public partial PageViewModel? CurrentPage { get; set; }
|
||||
|
||||
public CommandBarViewModel()
|
||||
public CommandBarViewModel(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
WeakReferenceMessenger.Default.Register<UpdateCommandBarMessage>(this);
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.CmdPal.Core.ViewModels.Models;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Models;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace Microsoft.CmdPal.Core.ViewModels;
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
|
||||
public partial class CommandContextItemViewModel(ICommandContextItem contextItem, WeakReference<IPageContext> context) : CommandItemViewModel(new(contextItem), context), IContextItemViewModel
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user