Repository: microsoft/terminal (language: cpp)

Issue:
Better icons for Visual Studio Developer Command Prompt/Powershell profiles
<!-- 
🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨

I ACKNOWLEDGE THE FOLLOWING BEFORE PROCEEDING:
1. If I delete this entire template and go my own path, the core team may close my issue without further explanation or engagement.
2. If I list multiple bugs/concerns in this one issue, the core team may close my issue without further explanation or engagement.
3. If I write an issue that has many duplicates, the core team may close my issue without further explanation or engagement (and without necessarily spending time to find the exact duplicate ID number).
4. If I leave the title incomplete when filing the issue, the core team may close my issue without further explanation or engagement.
5. If I file something completely blank in the body, the core team may close my issue without further explanation or engagement.

All good? Then proceed!
-->

# Description of the new feature/enhancement
Custom icons for the `Developer PowerShell for VS 2022` and `Developer Command Prompt for VS 2022` profiles would be a nice touch.

![5tXWrREqj0](https://github.com/user-attachments/assets/2456f899-1a19-4dfe-848c-e8543d339584)


<!-- 
A clear and concise description of what the problem is that the new feature would solve.
Describe why and how a user would use this new functionality (if applicable).
-->

# Proposed technical implementation details (optional)
Include images in the app's `ProfileIcons/` directory and automatically use them for said profiles. I threw these together in GIMP. If they are satisfactory, feel free to use them.

![cmd_vs](https://github.com/user-attachments/assets/1d3da7f9-1de7-40f1-9932-196fa10ec130)

![powershell5_vs](https://github.com/user-attachments/assets/1cb5ab60-31b2-4825-8666-74eca7b243fa)


<!-- 
A clear and concise description of what you want to happen.
-->



Discussion / hints:

Fix patch:
diff --git a/src/cascadia/CascadiaPackage/ProfileIcons/vs-cmd.scale-100.png b/src/cascadia/CascadiaPackage/ProfileIcons/vs-cmd.scale-100.png
new file mode 100644
index 00000000000..9e712ad8d95
Binary files /dev/null and b/src/cascadia/CascadiaPackage/ProfileIcons/vs-cmd.scale-100.png differ
diff --git a/src/cascadia/CascadiaPackage/ProfileIcons/vs-cmd.scale-150.png b/src/cascadia/CascadiaPackage/ProfileIcons/vs-cmd.scale-150.png
new file mode 100644
index 00000000000..82aa697c74f
Binary files /dev/null and b/src/cascadia/CascadiaPackage/ProfileIcons/vs-cmd.scale-150.png differ
diff --git a/src/cascadia/CascadiaPackage/ProfileIcons/vs-cmd.scale-200.png b/src/cascadia/CascadiaPackage/ProfileIcons/vs-cmd.scale-200.png
new file mode 100644
index 00000000000..ea8c240656d
Binary files /dev/null and b/src/cascadia/CascadiaPackage/ProfileIcons/vs-cmd.scale-200.png differ
diff --git a/src/cascadia/CascadiaPackage/ProfileIcons/vs-powershell.scale-100.png b/src/cascadia/CascadiaPackage/ProfileIcons/vs-powershell.scale-100.png
new file mode 100644
index 00000000000..54a8c126ff7
Binary files /dev/null and b/src/cascadia/CascadiaPackage/ProfileIcons/vs-powershell.scale-100.png differ
diff --git a/src/cascadia/CascadiaPackage/ProfileIcons/vs-powershell.scale-150.png b/src/cascadia/CascadiaPackage/ProfileIcons/vs-powershell.scale-150.png
new file mode 100644
index 00000000000..66486474a59
Binary files /dev/null and b/src/cascadia/CascadiaPackage/ProfileIcons/vs-powershell.scale-150.png differ
diff --git a/src/cascadia/CascadiaPackage/ProfileIcons/vs-powershell.scale-200.png b/src/cascadia/CascadiaPackage/ProfileIcons/vs-powershell.scale-200.png
new file mode 100644
index 00000000000..68aa47b458f
Binary files /dev/null and b/src/cascadia/CascadiaPackage/ProfileIcons/vs-powershell.scale-200.png differ
diff --git a/src/cascadia/CascadiaPackage/ProfileIcons/vs-pwsh.scale-100.png b/src/cascadia/CascadiaPackage/ProfileIcons/vs-pwsh.scale-100.png
new file mode 100644
index 00000000000..1fa174e5ac7
Binary files /dev/null and b/src/cascadia/CascadiaPackage/ProfileIcons/vs-pwsh.scale-100.png differ
diff --git a/src/cascadia/CascadiaPackage/ProfileIcons/vs-pwsh.scale-150.png b/src/cascadia/CascadiaPackage/ProfileIcons/vs-pwsh.scale-150.png
new file mode 100644
index 00000000000..57615ac57ee
Binary files /dev/null and b/src/cascadia/CascadiaPackage/ProfileIcons/vs-pwsh.scale-150.png differ
diff --git a/src/cascadia/CascadiaPackage/ProfileIcons/vs-pwsh.scale-200.png b/src/cascadia/CascadiaPackage/ProfileIcons/vs-pwsh.scale-200.png
new file mode 100644
index 00000000000..d7f31d5d715
Binary files /dev/null and b/src/cascadia/CascadiaPackage/ProfileIcons/vs-pwsh.scale-200.png differ
diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp
index 3d576f60603..6febe041d67 100644
--- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp
+++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp
@@ -20,9 +20,6 @@
 #include "SshHostGenerator.h"
 #endif
 
-// userDefault.h is like the above, but with a default template for the user's settings.json.
-#include <LegacyProfileGeneratorNamespaces.h>
-
 #include "ApplicationState.h"
 #include "DefaultTerminal.h"
 #include "FileUtils.h"
@@ -465,6 +462,11 @@ bool SettingsLoader::FixupUserSettings()
         CommandlinePatch{ DEFAULT_WINDOWS_POWERSHELL_GUID, L"powershell.exe", L"%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" },
     };
 
+    static constexpr std::array iconsToClearFromVisualStudioProfiles{
+        std::wstring_view{ L"ms-appx:///ProfileIcons/{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.png" },
+        std::wstring_view{ L"ms-appx:///ProfileIcons/{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.png" },
+    };
+
     auto fixedUp = userSettings.fixupsAppliedDuringLoad;
     fixedUp = userSettings.globals->FixupsAppliedDuringLoad() || fixedUp;
 
@@ -473,28 +475,39 @@ bool SettingsLoader::FixupUserSettings()
     {
         fixedUp = RemapColorSchemeForProfile(profile) || fixedUp;
 
-        if (!profile->HasCommandline())
+        if (profile->HasCommandline())
         {
-            continue;
+            for (const auto& patch : commandlinePatches)
+            {
+                if (profile->Guid() == patch.guid && til::equals_insensitive_ascii(profile->Commandline(), patch.before))
+                {
+                    profile->ClearCommandline();
+
+                    // GH#12842:
+                    // With the commandline field on the user profile gone, it's actually unknown what
+                    // commandline it'll inherit, since a user profile can have multiple parents. We have to
+                    // make sure we restore the correct commandline in case we don't inherit the expected one.
+                    if (profile->Commandline() != patch.after)
+                    {
+                        profile->Commandline(winrt::hstring{ patch.after });
+                    }
+
+                    fixedUp = true;
+                    break;
+                }
+            }
         }
 
-        for (const auto& patch : commandlinePatches)
+        if (profile->HasIcon() && profile->HasSource() && profile->Source() == VisualStudioGenerator::Namespace)
         {
-            if (profile->Guid() == patch.guid && til::equals_insensitive_ascii(profile->Commandline(), patch.before))
+            for (auto&& icon : iconsToClearFromVisualStudioProfiles)
             {
-                profile->ClearCommandline();
-
-                // GH#12842:
-                // With the commandline field on the user profile gone, it's actually unknown what
-                // commandline it'll inherit, since a user profile can have multiple parents. We have to
-                // make sure we restore the correct commandline in case we don't inherit the expected one.
-                if (profile->Commandline() != patch.after)
+                if (profile->Icon() == icon)
                 {
-                    profile->Commandline(winrt::hstring{ patch.after });
+                    profile->ClearIcon();
+                    fixedUp = true;
+                    break;
                 }
-
-                fixedUp = true;
-                break;
             }
         }
     }
diff --git a/src/cascadia/TerminalSettingsModel/Profile.cpp b/src/cascadia/TerminalSettingsModel/Profile.cpp
index 0f568b37e53..66382c857b1 100644
--- a/src/cascadia/TerminalSettingsModel/Profile.cpp
+++ b/src/cascadia/TerminalSettingsModel/Profile.cpp
@@ -329,7 +329,7 @@ Json::Value Profile::ToJson() const
     // Recall: Icon isn't actually a setting in the MTSM_PROFILE_SETTINGS. We
     // defined it manually in Profile, so make sure we only serialize the Icon
     // if the user actually changed it here.
-    JsonUtils::SetValueForKey(json, IconKey, (writeBasicSettings && HasIcon()) ? Icon() : _Icon);
+    JsonUtils::SetValueForKey(json, IconKey, _Icon);
 
     // PermissiveStringConverter is unnecessary for serialization
     JsonUtils::SetValueForKey(json, PaddingKey, _Padding);
diff --git a/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.cpp b/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.cpp
index 3fd3bca8a71..2fcfb1cc528 100644
--- a/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.cpp
+++ b/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.cpp
@@ -9,9 +9,11 @@
 
 using namespace winrt::Microsoft::Terminal::Settings::Model;
 
+std::wstring_view VisualStudioGenerator::Namespace{ L"Windows.Terminal.VisualStudio" };
+
 std::wstring_view VisualStudioGenerator::GetNamespace() const noexcept
 {
-    return std::wstring_view{ L"Windows.Terminal.VisualStudio" };
+    return Namespace;
 }
 
 void VisualStudioGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const
diff --git a/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.h b/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.h
index 766d2cd7775..ef36284db74 100644
--- a/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.h
+++ b/src/cascadia/TerminalSettingsModel/VisualStudioGenerator.h
@@ -26,6 +26,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model
     class VisualStudioGenerator : public IDynamicProfileGenerator
     {
     public:
+        static std::wstring_view Namespace;
         std::wstring_view GetNamespace() const noexcept override;
         void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const override;
 
diff --git a/src/cascadia/TerminalSettingsModel/VsDevCmdGenerator.h b/src/cascadia/TerminalSettingsModel/VsDevCmdGenerator.h
index dd5d42dba38..eab94e89546 100644
--- a/src/cascadia/TerminalSettingsModel/VsDevCmdGenerator.h
+++ b/src/cascadia/TerminalSettingsModel/VsDevCmdGenerator.h
@@ -41,7 +41,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model
 
         std::wstring GetProfileIconPath() const
         {
-            return L"ms-appx:///ProfileIcons/{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.png";
+            return L"ms-appx:///ProfileIcons/vs-cmd.png";
         }
 
         std::wstring GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance) const;
diff --git a/src/cascadia/TerminalSettingsModel/VsDevShellGenerator.h b/src/cascadia/TerminalSettingsModel/VsDevShellGenerator.h
index b7d14e8b951..7557b503c0b 100644
--- a/src/cascadia/TerminalSettingsModel/VsDevShellGenerator.h
+++ b/src/cascadia/TerminalSettingsModel/VsDevShellGenerator.h
@@ -38,7 +38,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model
 
         std::wstring GetProfileIconPath() const
         {
-            return L"ms-appx:///ProfileIcons/{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.png";
+            return L"ms-appx:///ProfileIcons/vs-powershell.png";
         }
 
         std::wstring GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance) const;


Test patch: