diff --git a/src/modules/peek/Peek.FilePreviewer/Controls/BrowserControl.xaml.cs b/src/modules/peek/Peek.FilePreviewer/Controls/BrowserControl.xaml.cs index 534cdb734c..c9ef112ea9 100644 --- a/src/modules/peek/Peek.FilePreviewer/Controls/BrowserControl.xaml.cs +++ b/src/modules/peek/Peek.FilePreviewer/Controls/BrowserControl.xaml.cs @@ -28,9 +28,6 @@ namespace Peek.FilePreviewer.Controls public event DOMContentLoadedHandler? DOMContentLoaded; - private string previewBrowserUserDataFolder = System.Environment.GetEnvironmentVariable("USERPROFILE") + - "\\AppData\\LocalLow\\Microsoft\\PowerToys\\Peek-Temp"; - public static readonly DependencyProperty SourceProperty = DependencyProperty.Register( nameof(Source), typeof(Uri), @@ -43,10 +40,40 @@ namespace Peek.FilePreviewer.Controls set { SetValue(SourceProperty, value); } } + public static readonly DependencyProperty IsDevFilePreviewProperty = DependencyProperty.Register( + nameof(IsDevFilePreview), + typeof(bool), + typeof(BrowserControl), + new PropertyMetadata(null, new PropertyChangedCallback((d, e) => ((BrowserControl)d).SourcePropertyChanged()))); + + public bool IsDevFilePreview + { + get + { + return (bool)GetValue(IsDevFilePreviewProperty); + } + + set + { + SetValue(IsDevFilePreviewProperty, value); + + if (PreviewBrowser.CoreWebView2 != null) + { + PreviewBrowser.CoreWebView2.Settings.IsScriptEnabled = value; + if (value) + { + PreviewBrowser.CoreWebView2.SetVirtualHostNameToFolderMapping(Microsoft.PowerToys.FilePreviewCommon.MonacoHelper.VirtualHostName, Microsoft.PowerToys.FilePreviewCommon.MonacoHelper.MonacoDirectory, CoreWebView2HostResourceAccessKind.Allow); + } + } + } + } + + public string? TempDataFolder { get; set; } + public BrowserControl() { this.InitializeComponent(); - Environment.SetEnvironmentVariable("WEBVIEW2_USER_DATA_FOLDER", previewBrowserUserDataFolder, EnvironmentVariableTarget.Process); + Environment.SetEnvironmentVariable("WEBVIEW2_USER_DATA_FOLDER", TempDataFolder, EnvironmentVariableTarget.Process); } public void Dispose() @@ -68,7 +95,7 @@ namespace Peek.FilePreviewer.Controls _navigatedUri = null; - if (Source != null) + if (Source != null && PreviewBrowser.CoreWebView2 != null) { /* CoreWebView2.Navigate() will always trigger a navigation even if the content/URI is the same. * Use WebView2.Source to avoid re-navigating to the same content. */ @@ -93,15 +120,23 @@ namespace Peek.FilePreviewer.Controls PreviewBrowser.CoreWebView2.Settings.AreHostObjectsAllowed = false; PreviewBrowser.CoreWebView2.Settings.IsGeneralAutofillEnabled = false; PreviewBrowser.CoreWebView2.Settings.IsPasswordAutosaveEnabled = false; - PreviewBrowser.CoreWebView2.Settings.IsScriptEnabled = false; + PreviewBrowser.CoreWebView2.Settings.IsScriptEnabled = IsDevFilePreview; PreviewBrowser.CoreWebView2.Settings.IsWebMessageEnabled = false; + if (IsDevFilePreview) + { + Logger.LogInfo("Set virtual host name to folder mapping: " + Microsoft.PowerToys.FilePreviewCommon.MonacoHelper.MonacoDirectory); + PreviewBrowser.CoreWebView2.SetVirtualHostNameToFolderMapping(Microsoft.PowerToys.FilePreviewCommon.MonacoHelper.VirtualHostName, Microsoft.PowerToys.FilePreviewCommon.MonacoHelper.MonacoDirectory, CoreWebView2HostResourceAccessKind.Allow); + } + PreviewBrowser.CoreWebView2.DOMContentLoaded += CoreWebView2_DOMContentLoaded; } catch (Exception ex) { Logger.LogError("WebView2 loading failed. " + ex.Message); } + + Navigate(); } private void CoreWebView2_DOMContentLoaded(CoreWebView2 sender, CoreWebView2DOMContentLoadedEventArgs args) diff --git a/src/modules/peek/Peek.FilePreviewer/Controls/TextFilePreview.xaml b/src/modules/peek/Peek.FilePreviewer/Controls/TextFilePreview.xaml deleted file mode 100644 index 701fd33648..0000000000 --- a/src/modules/peek/Peek.FilePreviewer/Controls/TextFilePreview.xaml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - diff --git a/src/modules/peek/Peek.FilePreviewer/Controls/TextFilePreview.xaml.cs b/src/modules/peek/Peek.FilePreviewer/Controls/TextFilePreview.xaml.cs deleted file mode 100644 index 7d06ae0122..0000000000 --- a/src/modules/peek/Peek.FilePreviewer/Controls/TextFilePreview.xaml.cs +++ /dev/null @@ -1,164 +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; -using CommunityToolkit.Mvvm.ComponentModel; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.Web.WebView2.Core; -using Peek.Common.Helpers; -using Windows.System; - -namespace Peek.FilePreviewer.Controls -{ - [INotifyPropertyChanged] - public sealed partial class TextFilePreview : UserControl, IDisposable - { - /// - /// Helper private Uri where we cache the last navigated page - /// so we can redirect internal PDF or Webpage links to external - /// webbrowser, avoiding WebView internal navigation. - /// - private Uri? _navigatedUri; - - public delegate void NavigationCompletedHandler(WebView2? sender, CoreWebView2NavigationCompletedEventArgs? args); - - public delegate void DOMContentLoadedHandler(CoreWebView2? sender, CoreWebView2DOMContentLoadedEventArgs? args); - - public event NavigationCompletedHandler? NavigationCompleted; - - public event DOMContentLoadedHandler? DOMContentLoaded; - - public static readonly DependencyProperty SourceProperty = DependencyProperty.Register( - nameof(Source), - typeof(Uri), - typeof(TextFilePreview), - new PropertyMetadata(null, new PropertyChangedCallback((d, e) => ((TextFilePreview)d).SourcePropertyChanged()))); - - public string? TempDataFolder { get; set; } - - public Uri? Source - { - get { return (Uri)GetValue(SourceProperty); } - set { SetValue(SourceProperty, value); } - } - - public TextFilePreview() - { - this.InitializeComponent(); - Environment.SetEnvironmentVariable("WEBVIEW2_USER_DATA_FOLDER", TempDataFolder, EnvironmentVariableTarget.Process); - } - - public void Dispose() - { - if (PreviewText.CoreWebView2 != null) - { - PreviewText.CoreWebView2.DOMContentLoaded -= CoreWebView2_DOMContentLoaded; - } - } - - /// - /// Navigate to the to the set in . - /// Calling will always trigger a navigation/refresh - /// even if web target file is the same. - /// - public void Navigate() - { - var value = Environment.GetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS"); - - _navigatedUri = null; - - if (Source != null && PreviewText.CoreWebView2 != null) - { - /* CoreWebView2.Navigate() will always trigger a navigation even if the content/URI is the same. - * Use WebView2.Source to avoid re-navigating to the same content. */ - PreviewText.CoreWebView2.Navigate(Source.ToString()); - } - } - - private void SourcePropertyChanged() - { - Navigate(); - } - - private async void PreviewText_Loading(FrameworkElement sender, object args) - { - if (PreviewText.CoreWebView2 == null) - { - try - { - await PreviewText.EnsureCoreWebView2Async(); - } - catch (Exception ex) - { - Logger.LogError("CoreWebView2 initialization failed. " + ex.Message); - } - } - } - - private void CoreWebView2_DOMContentLoaded(CoreWebView2 sender, CoreWebView2DOMContentLoadedEventArgs args) - { - DOMContentLoaded?.Invoke(sender, args); - } - - private async void PreviewText_NavigationStarting(WebView2 sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationStartingEventArgs args) - { - if (_navigatedUri == null) - { - return; - } - - // In case user starts or tries to navigate from within the HTML file we launch default web browser for navigation. - if (args.Uri != null && args.Uri != _navigatedUri?.ToString() && args.IsUserInitiated) - { - args.Cancel = true; - await Launcher.LaunchUriAsync(new Uri(args.Uri)); - } - } - - private void PreviewText_NavigationCompleted(WebView2 sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationCompletedEventArgs args) - { - if (args.IsSuccess) - { - _navigatedUri = Source; - } - - // OperationCanceled status code is used when the app cancels a navigation via NavigationStarting event - if (args.WebErrorStatus != CoreWebView2WebErrorStatus.OperationCanceled) - { - NavigationCompleted?.Invoke(sender, args); - } - } - - private void PreviewText_CoreWebView2Initialized(WebView2 sender, CoreWebView2InitializedEventArgs args) - { - try - { - sender.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false; - sender.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false; - sender.CoreWebView2.Settings.AreDevToolsEnabled = false; - sender.CoreWebView2.Settings.AreHostObjectsAllowed = false; - sender.CoreWebView2.Settings.IsGeneralAutofillEnabled = false; - sender.CoreWebView2.Settings.IsPasswordAutosaveEnabled = false; - sender.CoreWebView2.Settings.IsScriptEnabled = true; // TODO: enable only for dev files - sender.CoreWebView2.Settings.IsWebMessageEnabled = false; - - sender.CoreWebView2.SetVirtualHostNameToFolderMapping(Microsoft.PowerToys.FilePreviewCommon.MonacoHelper.VirtualHostName, Microsoft.PowerToys.FilePreviewCommon.MonacoHelper.MonacoDirectory, CoreWebView2HostResourceAccessKind.Allow); - - sender.CoreWebView2.DOMContentLoaded += CoreWebView2_DOMContentLoaded; - - if (Source != null) - { - /* CoreWebView2.Navigate() will always trigger a navigation even if the content/URI is the same. - * Use WebView2.Source to avoid re-navigating to the same content. */ - sender.CoreWebView2.Navigate(Source.ToString()); - } - } - catch (Exception ex) - { - Logger.LogError("WebView2 loading failed. " + ex.Message); - } - } - } -} diff --git a/src/modules/peek/Peek.FilePreviewer/FilePreview.xaml b/src/modules/peek/Peek.FilePreviewer/FilePreview.xaml index 1ce73960fe..73e36ac8ba 100644 --- a/src/modules/peek/Peek.FilePreviewer/FilePreview.xaml +++ b/src/modules/peek/Peek.FilePreviewer/FilePreview.xaml @@ -32,17 +32,10 @@ DOMContentLoaded="BrowserPreview_DOMContentLoaded" NavigationCompleted="PreviewBrowser_NavigationCompleted" Source="{x:Bind BrowserPreviewer.Preview, Mode=OneWay}" + TempDataFolder="{x:Bind BrowserPreviewer.TempDataFolder, Mode=OneTime}" + IsDevFilePreview="{x:Bind BrowserPreviewer.IsDevFilePreview, Mode=OneWay}" Visibility="{x:Bind IsPreviewVisible(BrowserPreviewer, Previewer.State), Mode=OneWay, FallbackValue=Collapsed}" /> - - Previewer as IBrowserPreviewer; - public ITextFilePreviewer? TextFilePreviewer => Previewer as ITextFilePreviewer; - public IUnsupportedFilePreviewer? UnsupportedFilePreviewer => Previewer as IUnsupportedFilePreviewer; public IFileSystemItem Item @@ -126,7 +123,7 @@ namespace Peek.FilePreviewer public bool IsWebPreview(IPreviewer? previewer, PreviewState? state) { - var isWebViewPreview = previewer != null && (previewer is ITextFilePreviewer || previewer is IBrowserPreviewer); + var isWebViewPreview = previewer != null && previewer is IBrowserPreviewer; return isWebViewPreview && MatchPreviewState(state, PreviewState.Loaded); } @@ -223,25 +220,6 @@ namespace Peek.FilePreviewer } } - private void TextPreview_DOMContentLoaded(CoreWebView2 sender, CoreWebView2DOMContentLoadedEventArgs args) - { - /* - * There is an odd behavior where the WebView2 would not raise the NavigationCompleted event - * for certain HTML files, even though it has already been loaded. Probably related to certain - * extra module that require more time to load. One example is saving and opening google.com locally. - * - * So to address this, we will make the Browser visible and display it as "Loaded" as soon the HTML document - * has been parsed and loaded with the DOMContentLoaded event. - * - * Similar issue: https://github.com/MicrosoftEdge/WebView2Feedback/issues/998 - */ - - if (TextFilePreviewer != null) - { - TextFilePreviewer.State = PreviewState.Loaded; - } - } - private void PreviewBrowser_NavigationCompleted(WebView2 sender, CoreWebView2NavigationCompletedEventArgs args) { /* @@ -261,25 +239,6 @@ namespace Peek.FilePreviewer } } - private void TextPreview_NavigationCompleted(WebView2 sender, CoreWebView2NavigationCompletedEventArgs args) - { - /* - * In theory most of navigation should work after DOM is loaded. - * But in case something fails we check NavigationCompleted event - * for failure and switch visibility accordingly. - * - * As an alternative, in the future, the preview control - * could also display error content. - */ - if (!args.IsSuccess) - { - if (TextFilePreviewer != null) - { - TextFilePreviewer.State = PreviewState.Error; - } - } - } - private async void KeyboardAccelerator_CtrlC_Invoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args) { if (Previewer != null) diff --git a/src/modules/peek/Peek.FilePreviewer/Peek.FilePreviewer.csproj b/src/modules/peek/Peek.FilePreviewer/Peek.FilePreviewer.csproj index 9fb3dd6301..ed348f9266 100644 --- a/src/modules/peek/Peek.FilePreviewer/Peek.FilePreviewer.csproj +++ b/src/modules/peek/Peek.FilePreviewer/Peek.FilePreviewer.csproj @@ -10,7 +10,6 @@ - @@ -51,10 +50,4 @@ - - - - MSBuild:Compile - - diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/Interfaces/IBrowserPreviewer.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/Interfaces/IBrowserPreviewer.cs index 8436b1e0cb..9ad5e8dc29 100644 --- a/src/modules/peek/Peek.FilePreviewer/Previewers/Interfaces/IBrowserPreviewer.cs +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/Interfaces/IBrowserPreviewer.cs @@ -9,5 +9,9 @@ namespace Peek.FilePreviewer.Previewers public interface IBrowserPreviewer : IPreviewer { public Uri? Preview { get; } + + public string TempDataFolder { get; } + + public bool IsDevFilePreview { get; } } } diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/Interfaces/ITextFilePreviewer.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/Interfaces/ITextFilePreviewer.cs deleted file mode 100644 index c9d5c8d685..0000000000 --- a/src/modules/peek/Peek.FilePreviewer/Previewers/Interfaces/ITextFilePreviewer.cs +++ /dev/null @@ -1,15 +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 Peek.FilePreviewer.Previewers -{ - public interface ITextFilePreviewer : IPreviewer - { - public Uri? Preview { get; } - - public string TempDataFolder { get; } - } -} diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/PreviewerFactory.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/PreviewerFactory.cs index a4b275248f..8a8b0becdc 100644 --- a/src/modules/peek/Peek.FilePreviewer/Previewers/PreviewerFactory.cs +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/PreviewerFactory.cs @@ -20,10 +20,6 @@ namespace Peek.FilePreviewer.Previewers { return new WebBrowserPreviewer(file); } - else if (TextFilePreviewer.IsFileTypeSupported(file.Extension)) - { - return new TextFilePreviewer(file); - } // Other previewer types check their supported file types here return CreateDefaultPreviewer(file); diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/TextFilePreviewer/TextFilePreviewer.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/TextFilePreviewer/TextFilePreviewer.cs deleted file mode 100644 index b3e1293af3..0000000000 --- a/src/modules/peek/Peek.FilePreviewer/Previewers/TextFilePreviewer/TextFilePreviewer.cs +++ /dev/null @@ -1,131 +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; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using CommunityToolkit.Mvvm.ComponentModel; -using Microsoft.UI.Dispatching; -using Peek.Common.Extensions; -using Peek.Common.Helpers; -using Peek.Common.Models; -using Windows.Foundation; - -namespace Peek.FilePreviewer.Previewers -{ - public partial class TextFilePreviewer : ObservableObject, ITextFilePreviewer, IDisposable - { - [ObservableProperty] - private Uri? preview; - - [ObservableProperty] - private PreviewState state; - - [ObservableProperty] - private string tempDataFolder = Environment.GetEnvironmentVariable("USERPROFILE") + - "\\AppData\\LocalLow\\Microsoft\\PowerToys\\Peek-Temp"; - - public TextFilePreviewer(IFileSystemItem file) - { - Item = file; - Dispatcher = DispatcherQueue.GetForCurrentThread(); - } - - public bool IsPreviewLoaded => preview != null; - - private IFileSystemItem Item { get; } - - private DispatcherQueue Dispatcher { get; } - - private Task? DisplayInfoTask { get; set; } - - public void Dispose() - { - Microsoft.PowerToys.FilePreviewCommon.Helper.CleanupTempDir(tempDataFolder); - GC.SuppressFinalize(this); - } - - public static bool IsFileTypeSupported(string fileExt) - { - return _supportedFileTypes.Contains(fileExt) || IsDevFile(fileExt); - } - - public static bool IsDevFile(string fileExt) - { - return _supportedMonacoFileTypes.Contains(fileExt); - } - - public Task GetPreviewSizeAsync(CancellationToken cancellationToken) - { - Size? size = new Size(1640, 1460); - return Task.FromResult(size); - } - - public async Task LoadPreviewAsync(CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - State = PreviewState.Loading; - await LoadDisplayInfoAsync(cancellationToken); - - if (HasFailedLoadingPreview()) - { - State = PreviewState.Error; - } - } - - public async Task CopyAsync() - { - await Dispatcher.RunOnUiThread(async () => - { - var storageItem = await Item.GetStorageItemAsync(); - ClipboardHelper.SaveToClipboard(storageItem); - }); - } - - public Task LoadDisplayInfoAsync(CancellationToken cancellationToken) - { - return TaskExtension.RunSafe(async () => - { - cancellationToken.ThrowIfCancellationRequested(); - - await Dispatcher.RunOnUiThread(async () => - { - var raw = await ReadHelper.Read(Item.Path.ToString()); - if (IsDevFile(Item.Extension)) - { - Preview = new Uri(MonacoHelper.PreviewTempFile(raw, Item.Extension, tempDataFolder)); - } - else - { - Preview = new Uri(WebViewHelper.PreviewTempFile(raw, Item.Path, tempDataFolder)); - } - }); - }); - } - - partial void OnPreviewChanged(Uri? value) - { - if (Preview != null) - { - State = PreviewState.Loaded; - } - } - - private bool HasFailedLoadingPreview() - { - var hasFailedLoadingDisplayInfo = !(DisplayInfoTask?.Result ?? true); - - return hasFailedLoadingDisplayInfo; - } - - private static readonly HashSet _supportedFileTypes = new HashSet - { - ".txt", - ".md", - }; - - private static readonly HashSet _supportedMonacoFileTypes = MonacoHelper.GetExtensions(); - } -} diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/TextFilePreviewer/Helpers/WebViewHelper.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/WebBrowserPreviewer/Helpers/MarkdownHelper.cs similarity index 96% rename from src/modules/peek/Peek.FilePreviewer/Previewers/TextFilePreviewer/Helpers/WebViewHelper.cs rename to src/modules/peek/Peek.FilePreviewer/Previewers/WebBrowserPreviewer/Helpers/MarkdownHelper.cs index 13fb7880e5..0c22124162 100644 --- a/src/modules/peek/Peek.FilePreviewer/Previewers/TextFilePreviewer/Helpers/WebViewHelper.cs +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/WebBrowserPreviewer/Helpers/MarkdownHelper.cs @@ -8,7 +8,7 @@ using Common.UI; namespace Peek.FilePreviewer.Previewers { - public class WebViewHelper + public class MarkdownHelper { /// /// Prepares temp html for the previewing diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/TextFilePreviewer/Helpers/MonacoHelper.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/WebBrowserPreviewer/Helpers/MonacoHelper.cs similarity index 100% rename from src/modules/peek/Peek.FilePreviewer/Previewers/TextFilePreviewer/Helpers/MonacoHelper.cs rename to src/modules/peek/Peek.FilePreviewer/Previewers/WebBrowserPreviewer/Helpers/MonacoHelper.cs diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/TextFilePreviewer/Helpers/ReadHelper.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/WebBrowserPreviewer/Helpers/ReadHelper.cs similarity index 100% rename from src/modules/peek/Peek.FilePreviewer/Previewers/TextFilePreviewer/Helpers/ReadHelper.cs rename to src/modules/peek/Peek.FilePreviewer/Previewers/WebBrowserPreviewer/Helpers/ReadHelper.cs diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/WebBrowserPreviewer/WebBrowserPreviewer.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/WebBrowserPreviewer/WebBrowserPreviewer.cs index f4f2aa3386..fd0436c3ca 100644 --- a/src/modules/peek/Peek.FilePreviewer/Previewers/WebBrowserPreviewer/WebBrowserPreviewer.cs +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/WebBrowserPreviewer/WebBrowserPreviewer.cs @@ -15,7 +15,7 @@ using Windows.Foundation; namespace Peek.FilePreviewer.Previewers { - public partial class WebBrowserPreviewer : ObservableObject, IBrowserPreviewer + public partial class WebBrowserPreviewer : ObservableObject, IBrowserPreviewer, IDisposable { private static readonly HashSet _supportedFileTypes = new HashSet { @@ -25,37 +25,89 @@ namespace Peek.FilePreviewer.Previewers // Document ".pdf", + + // Markdown + ".md", }; + private static readonly HashSet _supportedMonacoFileTypes = MonacoHelper.GetExtensions(); + [ObservableProperty] private Uri? preview; [ObservableProperty] private PreviewState state; + [ObservableProperty] + private string tempDataFolder = Environment.GetEnvironmentVariable("USERPROFILE") + + "\\AppData\\LocalLow\\Microsoft\\PowerToys\\Peek-Temp"; + + [ObservableProperty] + private bool isDevFilePreview; + public WebBrowserPreviewer(IFileSystemItem file) { File = file; Dispatcher = DispatcherQueue.GetForCurrentThread(); } + public void Dispose() + { + Microsoft.PowerToys.FilePreviewCommon.Helper.CleanupTempDir(tempDataFolder); + GC.SuppressFinalize(this); + } + private IFileSystemItem File { get; } + public bool IsPreviewLoaded => preview != null; + private DispatcherQueue Dispatcher { get; } + private Task? DisplayInfoTask { get; set; } + public Task GetPreviewSizeAsync(CancellationToken cancellationToken) { Size? size = null; return Task.FromResult(size); } - public Task LoadPreviewAsync(CancellationToken cancellationToken) + public async Task LoadPreviewAsync(CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); State = PreviewState.Loading; + await LoadDisplayInfoAsync(cancellationToken); - Preview = new Uri(File.Path); + if (HasFailedLoadingPreview()) + { + State = PreviewState.Error; + } + } - return Task.CompletedTask; + public Task LoadDisplayInfoAsync(CancellationToken cancellationToken) + { + return TaskExtension.RunSafe(async () => + { + cancellationToken.ThrowIfCancellationRequested(); + + await Dispatcher.RunOnUiThread(async () => + { + IsDevFilePreview = _supportedMonacoFileTypes.Contains(File.Extension); + if (IsDevFilePreview) + { + var raw = await ReadHelper.Read(File.Path.ToString()); + Preview = new Uri(MonacoHelper.PreviewTempFile(raw, File.Extension, tempDataFolder)); + } + else if (File.Extension == ".md") + { + var raw = await ReadHelper.Read(File.Path.ToString()); + Preview = new Uri(MarkdownHelper.PreviewTempFile(raw, File.Path, tempDataFolder)); + } + else + { + Preview = new Uri(File.Path); + } + }); + }); } public async Task CopyAsync() @@ -69,7 +121,12 @@ namespace Peek.FilePreviewer.Previewers public static bool IsFileTypeSupported(string fileExt) { - return _supportedFileTypes.Contains(fileExt); + return _supportedFileTypes.Contains(fileExt) || _supportedMonacoFileTypes.Contains(fileExt); + } + + private bool HasFailedLoadingPreview() + { + return !(DisplayInfoTask?.Result ?? true); } } }