/ src / modules / cmdpal / ext / SamplePagesExtension / Pages / SampleMarkdownImagesPage.cs
SampleMarkdownImagesPage.cs
  1  // Copyright (c) Microsoft Corporation
  2  // The Microsoft Corporation licenses this file to you under the MIT license.
  3  // See the LICENSE file in the project root for more information.
  4  
  5  #nullable enable
  6  
  7  using System;
  8  using System.Threading.Tasks;
  9  using Microsoft.CommandPalette.Extensions;
 10  using Microsoft.CommandPalette.Extensions.Toolkit;
 11  using Windows.Storage;
 12  
 13  namespace SamplePagesExtension.Pages;
 14  
 15  internal sealed partial class SampleMarkdownImagesPage : ContentPage
 16  {
 17      private static readonly Task InitializationTask;
 18  
 19      private static string? _sampleMarkdownText;
 20  
 21      static SampleMarkdownImagesPage()
 22      {
 23          InitializationTask = Task.Run(static async () =>
 24          {
 25              try
 26              {
 27                  // prepare data files
 28                  // 1) prepare something in our AppData Temp Folder
 29                  var spaceFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/Images/Space.png"));
 30                  var tempFile = await spaceFile!.CopyAsync(ApplicationData.Current!.TemporaryFolder!, "Space.png", NameCollisionOption.ReplaceExisting);
 31  
 32                  // 2) and also get an SVG directly from the package
 33                  var svgChipmunkFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/Images/FluentEmojiChipmunk.svg"));
 34  
 35                  _sampleMarkdownText = GetContentMarkup(
 36                      new Uri(tempFile.Path!, UriKind.Absolute),
 37                      new Uri(svgChipmunkFile.Path!, UriKind.Absolute));
 38              }
 39              catch (Exception ex)
 40              {
 41                  ExtensionHost.LogMessage(ex.ToString());
 42              }
 43          });
 44          return;
 45  
 46          static string GetContentMarkup(Uri path1, Uri path2)
 47          {
 48              return
 49            $$"""
 50              # Images in Markdown Content
 51              
 52              ## Available sources:
 53  
 54              - `![Alt Text](https://url)`
 55  
 56              - `![Alt Text](file://url)`
 57                - ℹ️ Only absolute paths are supported.
 58              
 59              - `![Alt Text](data:<mime>;[base64,]<data>)`
 60                - ⚠️ Only for small amount of data. Parsing large data blocks the UI.
 61  
 62              - `![Alt Text](ms-appx:///url)`
 63                - ⚠️ This loads from CmdPal's AppData folder, not Extension's, so it's not useful for extensions.
 64  
 65              - `![Alt Text](ms-appdata:///url)`
 66                - ⚠️ This loads from CmdPal's AppData folder, not Extension's, so it's not useful for extensions.
 67  
 68              ## Examples:
 69  
 70              ### Web URL
 71              ```xml
 72              ![painting](https://raw.githubusercontent.com/microsoft/PowerToys/refs/heads/main/doc/images/overview/Original/AdvancedPaste.png)
 73              ```
 74              ![painting](https://raw.githubusercontent.com/microsoft/PowerToys/refs/heads/main/doc/images/overview/Original/AdvancedPaste.png)
 75              
 76              ```xml
 77              ![painting](https://raw.githubusercontent.com/microsoft/PowerToys/refs/heads/main/doc/images/overview/Original/AdvancedPaste.png?--x-cmdpal-fit=fit)
 78              ```
 79              ![painting](https://raw.githubusercontent.com/microsoft/PowerToys/refs/heads/main/doc/images/overview/Original/AdvancedPaste.png?--x-cmdpal-fit=fit)
 80  
 81              ### File URL (PNG):
 82              ```xml
 83              ![green rectangle]({{path1}})
 84              ```
 85  
 86              ![green rectangle]({{path1}})
 87              
 88              ### File URL (SVG):
 89              ```xml
 90              ![chipmunk]({{path2}})
 91              ```
 92              
 93              ![chipmunk]({{path2}})
 94              
 95              ```xml
 96              ![chipmunk]({{path2}}?--x-cmdpal-maxwidth=400&--x-cmdpal-maxheight=400&--x-cmdpal-fit=fit)
 97              ```
 98              
 99              ![chipmunk]({{path2}}?--x-cmdpal-maxwidth=400&--x-cmdpal-maxheight=400&--x-cmdpal-fit=fit)
100              
101              ```xml
102              ![chipmunk]({{path2}}?--x-cmdpal-width=64)
103              ```
104              ![chipmunk]({{path2}}?--x-cmdpal-width=64)
105  
106              ## Data URL (PNG):
107              ⚠️ Passing large data into Markdown Content is unadvisable, parsing large data URLs can be slow and cause hangs.
108              ```xml
109              ![QR](data:image/png;base64,iVBORw0KGgoA...RU5ErkJggg==)
110              ```
111  
112              ![QR](data:image/png;base64,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)
113  
114              ### Data URL (SVG):
115              ⚠️ Passing large data into Markdown Content is unadvisable, parsing large data URLs can be slow and cause hangs.
116              ```xml
117              ![emoji](data:image/svg+xml;base64,PHN2ZyB4bWxucz0ia...NiAweiIvPjwvZz48L3N2Zz4=)
118              ```
119              
120              ![emoji](data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNTYiIGhlaWdodD0iMjU2IiB2aWV3Qm94PSIwIDAgMzIgMzIiPjxnIGZpbGw9Im5vbmUiPjxwYXRoIGZpbGw9IiNGRkIwMkUiIGQ9Ik0xNS45OTkgMjkuOTk4YzkuMzM0IDAgMTMuOTk5LTYuMjY4IDEzLjk5OS0xNGMwLTcuNzMtNC42NjUtMTMuOTk4LTE0LTEzLjk5OEM2LjY2NSAyIDIgOC4yNjggMiAxNS45OTlzNC42NjQgMTMuOTk5IDEzLjk5OSAxMy45OTkiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTAuNSAxOWE0LjUgNC41IDAgMSAwIDAtOWE0LjUgNC41IDAgMCAwIDAgOW0xMSAwYTQuNSA0LjUgMCAxIDAgMC05YTQuNSA0LjUgMCAwIDAgMCA5Ii8+PHBhdGggZmlsbD0iIzQwMkEzMiIgZD0iTTguMDcgNy45ODhjLS41OTQuNTYyLS45NTIgMS4yNC0xLjA5NiAxLjY3YS41LjUgMCAxIDEtLjk0OC0uMzE2Yy4xOS0uNTcuNjMtMS4zOTIgMS4zNTUtMi4wOEM4LjExMyA2LjU2NyA5LjE0OCA2IDEwLjUgNmEuNS41IDAgMCAxIDAgMWMtMS4wNDggMC0xLjg0Ni40MzMtMi40My45ODhNMTIgMTdhMiAyIDAgMSAwIDAtNGEyIDIgMCAwIDAgMCA0bTggMGEyIDIgMCAxIDAgMC00YTIgMiAwIDAgMCAwIDRtNS4wMjYtNy4zNDJjLS4xNDQtLjQzLS41MDMtMS4xMDgtMS4wOTUtMS42N0MyMy4zNDYgNy40MzMgMjIuNTQ4IDcgMjEuNSA3YS41LjUgMCAxIDEgMC0xYzEuMzUyIDAgMi4zODcuNTY3IDMuMTIgMS4yNjJjLjcyMy42ODggMS4xNjQgMS41MSAxLjM1NCAyLjA4YS41LjUgMCAxIDEtLjk0OC4zMTYiLz48cGF0aCBmaWxsPSIjQkIxRDgwIiBkPSJNMTMuMTcgMjJjLS4xMS4zMTMtLjE3LjY1LS4xNyAxdjJhMyAzIDAgMSAwIDYgMHYtMmMwLS4zNS0uMDYtLjY4Ny0uMTctMUwxNiAyMXoiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTMuMTcgMjJhMy4wMDEgMy4wMDEgMCAwIDEgNS42NiAweiIvPjwvZz48L3N2Zz4=)
121              
122              ### Data URL (SVG 2):
123              ⚠️ Passing large data into Markdown Content is unadvisable, parsing large data URLs can be slow and cause hangs.
124              ```xml
125              <img alt="emoji 2"
126              width="48"
127              height="48"
128              src="data:image/svg+xml;base64,PHN2ZyB....iIvPjwvZz48L3N2Zz4=" />
129              ```
130              
131              <img alt="emoji 2"
132                   width="48"
133                   height="48"
134                   src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNTYiIGhlaWdodD0iMjU2IiB2aWV3Qm94PSIwIDAgMzIgMzIiPjxnIGZpbGw9Im5vbmUiPjxwYXRoIGZpbGw9IiNGRkIwMkUiIGQ9Ik0xNS45OTkgMjkuOTk4YzkuMzM0IDAgMTMuOTk5LTYuMjY4IDEzLjk5OS0xNGMwLTcuNzMtNC42NjUtMTMuOTk4LTE0LTEzLjk5OEM2LjY2NSAyIDIgOC4yNjggMiAxNS45OTlzNC42NjQgMTMuOTk5IDEzLjk5OSAxMy45OTkiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTAuNSAxOWE0LjUgNC41IDAgMSAwIDAtOWE0LjUgNC41IDAgMCAwIDAgOW0xMSAwYTQuNSA0LjUgMCAxIDAgMC05YTQuNSA0LjUgMCAwIDAgMCA5Ii8+PHBhdGggZmlsbD0iIzQwMkEzMiIgZD0iTTguMDcgNy45ODhjLS41OTQuNTYyLS45NTIgMS4yNC0xLjA5NiAxLjY3YS41LjUgMCAxIDEtLjk0OC0uMzE2Yy4xOS0uNTcuNjMtMS4zOTIgMS4zNTUtMi4wOEM4LjExMyA2LjU2NyA5LjE0OCA2IDEwLjUgNmEuNS41IDAgMCAxIDAgMWMtMS4wNDggMC0xLjg0Ni40MzMtMi40My45ODhNMTIgMTdhMiAyIDAgMSAwIDAtNGEyIDIgMCAwIDAgMCA0bTggMGEyIDIgMCAxIDAgMC00YTIgMiAwIDAgMCAwIDRtNS4wMjYtNy4zNDJjLS4xNDQtLjQzLS41MDMtMS4xMDgtMS4wOTUtMS42N0MyMy4zNDYgNy40MzMgMjIuNTQ4IDcgMjEuNSA3YS41LjUgMCAxIDEgMC0xYzEuMzUyIDAgMi4zODcuNTY3IDMuMTIgMS4yNjJjLjcyMy42ODggMS4xNjQgMS41MSAxLjM1NCAyLjA4YS41LjUgMCAxIDEtLjk0OC4zMTYiLz48cGF0aCBmaWxsPSIjQkIxRDgwIiBkPSJNMTMuMTcgMjJjLS4xMS4zMTMtLjE3LjY1LS4xNyAxdjJhMyAzIDAgMSAwIDYgMHYtMmMwLS4zNS0uMDYtLjY4Ny0uMTctMUwxNiAyMXoiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTMuMTcgMjJhMy4wMDEgMy4wMDEgMCAwIDEgNS42NiAweiIvPjwvZz48L3N2Zz4=" />
135              
136              ### MS-APPX URL:
137              ⚠️ This loads from CmdPal's AppData folder, not Extension's, so it's not useful for extensions.
138              ```xml
139              ![Swirl](ms-appx:///Assets/Square44x44Logo.png)
140              ```
141              
142              ![Swirl](ms-appx:///Assets/Square44x44Logo.png)
143              
144              ### MS-APPDATA URL:
145              ⚠️ This loads from CmdPal's AppData folder, not Extension's, so it's not useful for extensions.
146              ```xml
147              ![Space](ms-appdata:///temp/Space.png)
148              ```
149              
150              ---
151              
152              # Scaling
153              
154              For URIs that support query parameters (file, http, ms-appx, ms-appdata), you can provide hints to control scaling
155              
156              - `--x-cmdpal-fit`
157                - `none`: no automatic scaling, provides image as is (default)
158                - `fit`: scale to fit the available space
159              - `--x-cmdpal-upscale`
160                - `true`: allow upscaling
161                - `false`: downscale only (default)
162              - `--x-cmdpal-width`: desired width in pixels
163              - `--x-cmdpal-height`: desired height in pixels
164              - `--x-cmdpal-maxwidth`: max width in pixels
165              - `--x-cmdpal-maxheight`: max height in pixels 
166              
167              Currently no support for data: scheme as it doesn't support query parameters at all.
168              
169              ## Examples
170              
171              ### No scaling
172              ```xml
173              ![green rectangle]({{path1}})
174              ```
175              
176              ![green rectangle]({{path1}})
177              
178              ### Scale to fit (scaling down only by default)
179              ```xml
180              ![green rectangle]({{path1}}?--x-cmdpal-fit=fit)
181              ```
182  
183              ![green rectangle]({{path1}}?--x-cmdpal-fit=fit)
184              
185              ### Scale to fit (in both direction)
186              ```xml
187              ![green rectangle]({{path1}}?--x-cmdpal-fit=fit&--x-cmdpal-upscale=true)
188              ```
189              
190              ![green rectangle]({{path1}}?--x-cmdpal-fit=fit&--x-cmdpal-upscale=true)
191              
192              ### Scale to exact width
193              ```xml
194              ![green rectangle]({{path1}}?--x-cmdpal-width=320)
195              ```
196              
197              ![green rectangle]({{path1}}?--x-cmdpal-width=320)
198              """;
199          }
200      }
201  
202      private string _currentContent;
203  
204      public SampleMarkdownImagesPage()
205      {
206          Name = "Sample Markdown with Images Page";
207          _currentContent = "Initializing...";
208          IsLoading = true;
209  
210          _ = InitializationTask!.ContinueWith(_ =>
211          {
212              _currentContent = _sampleMarkdownText!;
213              RaiseItemsChanged();
214              IsLoading = false;
215          });
216      }
217  
218      public override IContent[] GetContent() => [new MarkdownContent(_currentContent ?? string.Empty)];
219  }