This commit is a minimal fix in order to pass the
`IDWriteFontCollection` we create out of .ttf files residing next to our
binaries to the `IDWriteFontFallback::MapCharacters` call. The
`IDWriteTextFormat` is used in order to carry the font collection over
into `CustomTextLayout`.
## Validation
* Put `JetBrainsMono-Regular.ttf` into the binary output directory
* Modify `HKCU:\Console\*\FaceName` to `JetBrains Mono`
* Launch OpenConsole.exe
* OpenConsole uses JetBrains Mono ✔️Closes#11032Closes#11648
This commit introduces "AtlasEngine", a new text renderer based on DxEngine.
But unlike it, DirectWrite and Direct2D are only used to rasterize glyphs.
Blending and placing these glyphs into the target view is being done using
Direct3D and a simple HLSL shader. Since this new renderer more aggressively
assumes that the text is monospace, it simplifies the implementation:
The viewport is divided into cells, and its data is stored as a simple matrix.
Modifications to this matrix involve only simple pointer arithmetic and is easy
to understand. But just like with DxEngine however, DirectWrite
related code remains extremely complex and hard to understand.
Supported features:
* Basic text rendering with grayscale AA
* Foreground and background colors
* Emojis, including zero width joiners
* Underline, dotted underline, strikethrough
* Custom font axes and features
* Selections
* All cursor styles
* Full alpha support for all colors
* _Should_ work with Windows 7
Unsupported features:
* A more conservative GPU memory usage
The backing texture atlas for glyphs is grow-only and will not shrink.
After 256MB of memory is used up (~20k glyphs) text output
will be broken until the renderer is restarted.
* ClearType
* Remaining gridlines (left, right, top, bottom, double underline)
* Hyperlinks don't get full underlines if hovered in WT
* Softfonts
* Non-default line renditions
Performance:
* Runs at up to native display refresh rate
Unfortunately the frame rate often drops below refresh rate, due us
fighting over the buffer lock with other parts of the application.
* CPU consumption is up to halved compared to DxEngine
AtlasEngine is still highly unoptimized. Glyph hashing
consumes up to a third of the current CPU time.
* No regressions in WT performance
VT parsing and related buffer management takes up most of the CPU time (~85%),
due to which the AtlasEngine can't show any further improvements.
* ~2x improvement in raw text throughput in OpenConsole
compared to DxEngine running at 144 FPS
* ≥10x improvement in colored VT output in WT/OpenConsole
compared to DxEngine running at 144 FPS
In #11180 we made `opacity` independent from `useAcrylic`. We also changed the mouse wheel behavior to only change opacity, and not mess with acrylic.
However, on Windows 10, vintage opacity doesn't work at all. So there, we still need to manually enable acrylic when the user requests opacity.
* [x] Closes#11285
SUI changes in action:
![auto-acrylic-win10](https://user-images.githubusercontent.com/18356694/136281935-db9a10f4-e0ad-4422-950b-0a01dc3e12c0.gif)
Just like in #9760, we can't actually use the UWP file picker API, because it will absolutely not work at all when the Terminal is running elevated. That would prevent the picker from appearing at all. So instead, we'll just use the shell32 one manually.
This also gets rid of the confirmation dialog, since the team felt we didn't really need that. We could maybe replace it with a Toast (#8592), but _meh_
* [x] closes#11356
* [x] closes#11358
* This is a lot like #9760
* introduced in #11062
* megathread: #9700
Re-enables the delete button for generated profiles in the settings UI.
Additionally fixes "Startup Profiles" to only list active profiles.
Profiles are considered deleted if they're absent from settings.json, but their
GUID has been encountered before. Or in other words, from a user's perspective:
Generated profiles are added to the settings.json automatically only once.
Thus if the user chooses to delete the profile (e.g. using the delete button)
they aren't re-added automatically and thus appear to have been deleted.
Meanwhile those generated profiles are actually only marked as "hidden"
as well as "deleted", but still exist in internal profile lists.
The "hidden" attribute hides them from all existing menus. The "deleted" one
hides them from the settings UI and prevents them from being written to disk.
It would've been preferrable of course to just not generate and
add deleted profile to internal profile lists in the first place.
But this would've required far more wide-reaching changes.
The settings UI for instance requires a list of _all_ profiles in order to
allow a user to re-create previously deleted profiles. Such an approach was
attempted but discarded because of it's current complexity overhead.
## References
* Part of #9997
* A sequel to 5d36e5d
## PR Checklist
* [x] Closes#10960
* [x] I work here
* [x] Tests added/passed
## Validation Steps Performed
* "Startup Profiles" doesn't list deleted profiles ✔️
* Manually removing an item from settings.json removes the profile ✔️
* Removing cmd.exe and saving doesn't create empty objects (#10960) ✔️
* "Add a new profile" lists deleted profiles ✔️
* "Duplicate" recreates previously deleted profiles ✔️
* Profiles are always created with GUIDs ✔️
## Summary of the Pull Request
Sends the additional xaml notification when the user presses the '+' or delete button for unfocused appearances
## PR Checklist
* [x] Closes#10673
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [ ] Schema updated.
* [x] I work here
## Validation Steps Performed
It works now
## Summary of the Pull Request
Adds unfocused appearance creation/configuration in the SUI
There is now an 'Unfocused Appearance' section at the bottom of the 'Appearance' tab in a profile. There is a '+' button to create an unfocused appearance if one does not exist, or a delete button to delete the unfocused appearance if one exists (only one of these buttons is visible at a time).
## PR Checklist
* [ ] Closes #xxx
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [ ] Schema updated.
* [x] I work here
## Validation Steps Performed
![unfocusedSUI](https://user-images.githubusercontent.com/26824113/125523613-48aefe28-b4cf-46a2-91c9-2ba3ea89e071.gif)
This pull request brings back the "Base Layer" page, now renamed to
"Defaults", and the "Reset to inherited value" buttons. The scope of
inheritance for which buttons will display has been widened.
The button will be visible in the following cases:
The user has set a setting for the current profile, and it overrides...
1. ... something in profiles.defaults.
2. ... something in a Fragment Extension profile.
3. ... something from a Dynamic Profile Generator.
4. ... something from the compiled-in defaults.
Compared to the original implementation of reset arrows, cases (1), (3)
and (4) are new. Rationale:
(1) The user can see a setting on the Defaults page, and they need a way
to reset back to it.
(3) Dynamic profiles are not meaningfully different from fragments, and
users may need a way to reset back to the default value generated
for WSL or PowerShell.
(4) The user can see a setting on the Defaults page, **BUT** they are
not the one who created it. They *still* need a way to get back to
it.
To support this, I've introduced another origin tag, "User", and renamed
"Custom" to "None". Due to the way origin/override detection works¹, we
cannot otherwise disambiguate between settings that came from the user
and settings that came from the compiled-in defaults.
Changes were required in TerminalSettings such that we could construct a
settings object with a profile that does not have a GUID. In making this
change, I fixed a bit of silliness where we took a profile, extracted
its guid, and used that guid to look up the same profile object. Oops.
I also fixed the PropertyChanged notifier to include the
XxxOverrideSource property.
The presence of the page and the reset arrows is restricted to
Preview- or Dev-branded builds. Stable builds will retain their current
behavior.
¹ `XxxOverrideSource` returns the profile *above* the current profile
that holds a value for setting `Xxx`. When the value is the
compiled-in value, `XxxOverrideSource` will be `null`. Since it's
supposed to be the profile above the current profile, it will also be
`null` if the profile contains a setting at this layer.
In short, `null` means "user specified" *or* "compiled in". Oops.
Fixes#10430
Validation
----------
* [x] Tested Release build to make sure it's mostly arrow-free (apart from fragments)
Implements an `Appearances` xaml object and an `AppearanceViewModel` in the SettingsEditor project. Updates `Profiles` to use these new objects for its default appearance.
This is the first step towards getting `UnfocusedAppearance` into the SUI.
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request
Adds a new bellStyle called `window`. When `window` is set and a BEL is emitted, we flash the pane that emitted it.
Additionally, changes bellStyle in the SUI to a list of checkboxes instead of radio buttons, to match bellStyle being a flag-enum. Deprecates 'BellStyle::Visual' in the schema, but still allows it to be set in the json (it maps to `Window | Taskbar`)
<!-- Other than the issue solved, is this relevant to any other issues/existing PRs? -->
## References
#6700
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [ ] Closes #xxx
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [ ] Schema updated.
* [x] I work here
<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
GIF in Teams
## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/issues/8881
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already.
## Detailed Description of the Pull Request / Additional comments
* Preserve profile GUID upon item Tag creation.
* Use this GUID rather than the current profile GUID to select an item
upon settings update.
So even if the profile was renamed and the GUID has changed,
the GUID in the tag remains unchanged and can be found
upon discarding.
## Summary of the Pull Request
Remove an unnecessary check in `Profiles.cpp` that was preventing us from enabling the text box and browse button when the user unchecks 'use parent process directory'
## PR Checklist
* [x] Closes#9847
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [ ] Schema updated.
* [x] I work here
## Validation Steps Performed
Played around with it and it works.
This is required to maintain the modality of the dialogs, which we lost
when we moved from Pickers to IFileDialog. The HWND hosting Window API
we dreamed up is incompatible with IModalDialog, because IModalDialog
requires the HWND immediately upon `Show`. We're smuggling it in a
uint64, as is tradition.
zadjii-msft noticed this in #9760.
Using Pickers from an elevated application yields an
ERROR_ACCESS_DENIED. Of course it does: it was designed for the modern
app platform.
Using the common dialog infrastructure has some downsides¹, but it
doesn't crash and is just as flexible.
I've added some fun templated functions that help us with the
complexity.
Fixes#8957
¹You've got to use raw COM, and it runs in-proc instead of out-of-proc.
## Validation Steps Performed
I tested every picker.
**BE NOT AFRAID**. I know that there's 107 files in this PR, but almost
all of it is just find/replacing `TerminalControl` with `Control`.
This is the start of the work to move TermControl into multiple pieces,
for #5000. The PR starts this work by:
* Splits `TerminalControl` into separate lib and dll projects. We'll
want control tests in the future, and for that, we'll need a lib.
* Moves `ICoreSettings` back into the `Microsoft.Terminal.Core`
namespace. We'll have other types in there soon too.
* I could not tell you why this works suddenly. New VS versions? New
cppwinrt version? Maybe we're just better at dealing with mdmerge
bugs these days.
* RENAMES `Microsoft.Terminal.TerminalControl` to
`Microsoft.Terminal.Control`. This touches pretty much every file in
the sln. Sorry about that (not sorry).
An upcoming PR will move much of the logic in TermControl into a new
`ControlCore` class that we'll add in `Microsoft.Terminal.Core`.
`ControlCore` will then be unittest-able in the
`UnitTests_TerminalCore`, which will help prevent regressions like #9455
## Detailed Description of the Pull Request / Additional comments
You're really gonna want to clean the sln first, then merge this into
your branch, then rebuild. It's very likely that old winmds will get
left behind. If you see something like
```
Error MDM2007 Cannot create type
Microsoft.Terminal.TerminalControl.KeyModifiers in read-only metadata
file Microsoft.Terminal.TerminalControl.
```
then that's what happened to you.
## Summary of the Pull Request
This replaces the Profiles > Font Face text box with a combo box.
## References
#6800 - Settings UI Epic
## Detailed Description of the Pull Request / Additional comments
- Enumerating the fonts
- [This doc](https://docs.microsoft.com/en-us/windows/win32/directwrite/font-enumeration) was the main reference used to enumerate the fonts. It was mildly adapted to use WinRT instead of WRL.
- Updating the UI
- Similar to other combo box settings, `Profiles` keeps a reference to the current value. We use that as a way to update the settings model. If an invalid value is used, we fallback to `Cascadia Mono`.
- A checkbox was added to let the user select from all of the installed fonts, or just the monospace ones.
## Demo
![Font Face Combo Box Demo](https://user-images.githubusercontent.com/11050425/109342917-6cd3b600-7821-11eb-8df9-fb988b037e02.gif)
## Summary of the Pull Request
Introduces the `SettingContainer`. `SettingContainer` is used to wrap a setting in the settings UI and provide the following functionality:
- a reset button next to the header
- tooltips and automation properties for the setting being wrapped
- a comment stating if you are currently overriding a setting
## References
[Spec - Inheritance in Settings UI](https://github.com/microsoft/terminal/blob/main/doc/specs/%231564%20-%20Settings%20UI/cascading-settings.md)
#8804 - removes the ambiguity of leaving a setting blank
#6800 - Settings UI Epic
#8899 - Automation properties for Settings UI
#8768 - Keyboard Navigation
## PR Checklist
* [X] Closes#8804
## Detailed Description of the Pull Request / Additional comments
A few highlights in this PR:
- CommonResources.xaml:
- we need to merge the SettingContainerStyle.xaml in there. Otherwise, XAML doesn't merge these files properly and can't apply the template.
- Profiles.cpp:
- view model checks if the starting directory and background image were reset, to determine which value to show when unchecking the special value
- `Profiles::OnNavigatedTo()` needs a property changed handler to update its own "Current<Setting>" and update the UI properly
- Profiles.xaml:
- basically wrapped all of the settings we want to be inheritable in there
- `Binding` is used instead of `x:Bind` in some places because `x:Bind` can't find the parent `SettingContainer` and gives you a compiler error.
- Resources.resw:
- had to set the "HeaderText" and "HelpText" on each setting container. Does a decent localization burden, unfortunately.
- `SettingContainer` files
- This operates by creating a template and applying that template over other settings. This allows you to inject the existing controls inside of this. This means that we need to provide our UIElements names and access/modify them via `OnApplyTemplate`
- We had to remove the header from each individual control, and have `SettingContainer` be in charge of it. This allows us to add the reset button in there.
- Due to the problem mentioned earlier about CommonResources.xaml, we can't reference anything from CommonResources.xaml.
- Using `DependencyProperty` to let us set a few properties in the XML files. Particularly, `Has<Setting>` and `Clear<Setting>` are what do all the heavy lifting of interacting with the inheritance model.
## Demo
![Inheritance Demo](https://user-images.githubusercontent.com/11050425/106192086-92a56680-6160-11eb-838c-4ec0beb54965.gif)
## Validation Steps Performed
- Verified correct binding behavior with the following generic setting controls:
- radio buttons
- toggle switch
- text block
- slider
- settings with browse buttons
- the background image alignment control
- controls with special check boxes (starting directory and background image)
## Next Steps
- The automation properties have been verified using NVDA. This is a part of resolving #8899.
- The override text is currently "Overrides a setting". According to #8269, we actually want to add a hyperlink in there that navigates to the parent profile object. This will be a follow-up task as it requires settings model changes.
This reverts the revert in #8838.
The problem was that the `Profile` in the singleton nav state would be
updated before the binding fired, so we'd end up modifying the _new_
profile, because both the old page and the new page would be pointing at
the _new_ profile already.
Instead of using a singleton instance of the profile nav state, we'll
create a new one each time. The new nav state attempt to steal the
selected pivot from the last instance of the nav state, if the profiles
are the same. This means that
This means that we won't end up modifying the new profile. The old
page's nav state will still have the old profile, so it'll still end up
modifying the old `ProfileViewModel`.
## PR Checklist
* [x] I work here
* [x] Tested manually
* [x] Fixes the first point in #8769, again
Make the "use parent process directory" checkbox rely on a computed
property in the ProfileViewModel. It will be enabled when the starting
directory is empty and disabled when it's not. When it's unchecked, the
last-used value will be restored. If there is no last-used value, it
will be set to %USERPROFILE%.
Closes#8805
This reverts commit a7d7362b95 introduced by #8803.
Reverting this commit fixes#8836 at the expense of the profile page
remembering the last Pivot selection. The
## References
#6800 - Settings UI Epic
#8803 maintained a `ProfilePageNavigationState` in `MainPage` to remember
the pivot position. However, the two-way binding on the TextBoxes
now seem to happen too late (after the navigation occurs),
resulting in the text being applied to the wrong profile. In other
words, the sequence of events probably looks something like this:
1. user types text (_state.profile = old profile)
2. user moves to new profile
3. navigation completes (_state.profile = new profile)
4. textbox two-way binding fires, setting _state.profile.WHATEVER = value
## Validation Steps Performed
Performed repro sets from #8836. Bug no longer occurs.
Reopens#8769Closes#8836
This PR Makes sure that after you save the settings, we stay on the same part of the profiles pivot. We do this by having a singleton `ProfilesNavigationState`, a bit like the color scheme one in #8799. Hence why this PR is targeting the other.
## PR Checklist
* [x] I work here
* [x] Tested manually
* [x] Fixes the first point in #8769
## Summary of the Pull Request
Two parts:
* Hide the BG image settings when no image is specified
* Add a checkbox for "Use desktop wallpaper". When that's checked, the BG image path input is hidden. Unchecking that box restores the path to what it was before.
## PR Checklist
* [x] Closes#8763
* [x] I work here
## Validation Steps Performed
Tested manually
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request
Add checkbox for 'inherit from parent process' for starting directory
When checked, the textbox and browse button are disabled
If the starting directory is empty, the checkbox is automatically checked
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Closes#8761
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [ ] Schema updated.
* [x] I work here
<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
<img width="328" alt="pardir1" src="https://user-images.githubusercontent.com/26824113/104529798-64038980-55bf-11eb-93fd-75e6cf1e2547.png">
<img width="317" alt="pardir2" src="https://user-images.githubusercontent.com/26824113/104529803-66fe7a00-55bf-11eb-89b6-5b35c8ab89b8.png">
Sorts the list of `EnumEntry`s that is used to create combo boxes and
radio buttons in the Settings UI. `INITIALIZE_BINDABLE_ENUM_SETTING`
sorts the list in increasing order of the enum values, whereas
`INITIALIZE_BINDABLE_ENUM_SETTING_REVERSE_ORDER` does so in decreasing
order.
## References
#6800 - Settings UI Epic
I attempted sorting the `IObservableVector<EnumEntry>` using
`std::sort`, but I would get an error in
`winrt::Windows::Foundation::swap` (`C2665`). So instead, I did the
following approach:
- (unchanged) we're converting the `IMap` from EnumMappings into (1) a
map of localized strings and (2) the list for XAML controls
- instead of storing `EnumEntry`s to the `IObservableVector` directly,
store it to a `std::vector`
- sort the vector using `std::sort`
- _now_ initialize the `IObservableVector` using the sorted
`std::vector`
This uses the value of the associated enum to determine a sorting order.
Since we want the "negative" value (i.e. "none" or "hidden") to be last,
I use `EnumEntryComparator` and `EnumEntryReverseComparator` to
determine whether we want increasing or decreasing order respectively.
`INITIALIZE_BINDABLE_ENUM_SETTING_REVERSE_ORDER` is a copy of
`INITIALIZE_BINDABLE_ENUM_SETTING`, except it uses
`EnumEntryReverseComparator` to sort in decreasing order.
Closes#8758
![image](https://user-images.githubusercontent.com/18356694/104447754-d6de1780-5561-11eb-8d99-71f4ec5ad429.png)
Put some settings below the acrylic & background image settings. Those groups have controls that will become visible when the user enables the setting. If there's other controls below them, then it's less likely that the user has _exactly scrolled to the checkbox_. That means it's more likely that the newly visible controls will be on-screen.
* [x] closes one checkbox in #8764
* [x] I work here
Introduces the following UI controls to the ColorSchemes page:
- "Add new" button
- next to dropdown selector
- adds a new color scheme named ("Color Scheme #" where # is the number of color schemes you have)
- "Rename" Button
- next to the selector
- replaces the ComboBox with a TextBox and the accept/cancel buttons appear
- "Delete" button
- bottom of the page
- opens flyout, when confirmed, deletes the current color scheme and selects another one
This also adds a Delete button to the Profiles page. The Hide checkbox was moved above the Delete button.
## References
#1564 - Settings UI
#6800 - Settings UI Completion Epic
## Detailed Description of the Pull Request / Additional comments
**Color Schemes:**
- Deleting a color scheme selects another one from the list available
- Rename replaces the combobox with a textbox to allow editing
- The Add New button creates a new color scheme named "Color Scheme X" where X is the number of schemes defined
- In-box color schemes cannot be deleted
**Profile:**
- Deleting a profile selects another one from the list available
- the rename button does not exist (yet), because it needs a modification to the NavigationView's Header Template
- The delete button is disabled for in-box profiles (CMD and Windows Powershell) and dynamic profiles
## Validation Steps Performed
**Color Schemes - Add New**
✅ Creates a new color scheme named "Color Scheme X" (X being the number of color schemes)
✅ The new color scheme can be renamed/deleted/modified
**Color Schemes - Rename**
✅ You cannot rename an in-box color scheme
✅ The rename button has a tooltip
✅ Clicking the rename button replaces the combobox with a textbox
✅ Accept --> changes name
✅ Cancel --> does not change the name
✅ accepting/cancelling the rename operation updates the combo box appropriately
**Color Schemes - Delete**
✅ Clicking delete produces a flyout to confirm deletion
✅ Deleting a color scheme removes it from the list and select the one under it
✅ Deleting the last color scheme selects the last available color scheme after it's deleted
✅ In-box color schemes have the delete button disabled, and a disclaimer appears next to it
**Profile- Delete**
✅ Base layer presents a disclaimer at the top, and hides the delete button
✅ Dynamic and in-box profiles disable the delete button and show the appropriate disclaimer next to the disabled button
✅ Clicking delete produces a flyout to confirm deletion
✅ Regular profiles have a delete button that is styled appropriately
✅ Clicking the delete profile button opens a content dialog. Confirmation deletes the profile and navigates to the profile indexed under it (deleting the last one redirects to the last one)
## Demo
Refer to this post [here](https://github.com/microsoft/terminal/pull/8403#issuecomment-747545651.
Confirmation flyout demo: https://github.com/microsoft/terminal/pull/8403#issuecomment-747657842
This commit introduces the terminal settings editor (to wit: the
Settings UI) as a standalone project. This project, and this commit, is
the result of two and a half months of work.
TSE started as a hackathon project in the Microsoft 2020 Hackathon, and
from there it's grown to be a bona-fide graphical settings editor.
There is a lot of xaml data binding in here, a number of views and a
number of view models, and a bunch of paradigms that we've been
reviewing and testing out and designing and refining.
Specified in #6720, #8269
Follow-up work in #6800Closes#1564Closes#8048 (PR)
Co-authored-by: Carlos Zamora <carlos.zamora@microsoft.com>
Co-authored-by: Kayla Cinnamon <cinnamon@microsoft.com>
Co-authored-by: Alberto Medina Gutierrez <almedina@microsoft.com>
Co-authored-by: John Grandle <jograndl@microsoft.com>
Co-authored-by: xerootg <xerootg@users.noreply.github.com>
Co-authored-by: Scott <sarmiger1@gmail.com>
Co-authored-by: Vineeth Thomas Alex <vineeththomasalex@gmail.com>
Co-authored-by: Leon Liang <lelian@microsoft.com>
Co-authored-by: Dustin L. Howett <duhowett@microsoft.com>
Signed-off-by: Dustin L. Howett <duhowett@microsoft.com>