diff --git a/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/.gitattributes b/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/.gitattributes
new file mode 100644
index 0000000..8bca8e4
--- /dev/null
+++ b/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/.gitattributes
@@ -0,0 +1,8 @@
+# Ignore HTML files in Resources and subdirectories
+Resources/**/*.html linguist-vendored
+
+# Ignore CSS files in Resources and subdirectories
+Resources/**/*.css linguist-vendored
+
+# Ignore JS files in Resources and subdirectories
+Resources/**/*.js linguist-vendored
diff --git a/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/ChmWebpagePanel.cs b/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/ChmWebpagePanel.cs
new file mode 100644
index 0000000..c9dc326
--- /dev/null
+++ b/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/ChmWebpagePanel.cs
@@ -0,0 +1,125 @@
+// Copyright © 2017-2026 QL-Win Contributors
+//
+// This file is part of QuickLook program.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+using QuickLook.Plugin.HtmlViewer;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+
+namespace QuickLook.Plugin.ChmViewer;
+
+public class ChmWebpagePanel : WebpagePanel
+{
+ private const string _resourcePrefix = "QuickLook.Plugin.ChmViewer.Resources.";
+ private static readonly Dictionary _resources = [];
+ private readonly byte[] _homePage;
+
+ static ChmWebpagePanel()
+ {
+ InitializeResources();
+ }
+
+ public ChmWebpagePanel()
+ {
+ _homePage = ReadResource("/chm2html.html") ?? [];
+ }
+
+ private static void InitializeResources()
+ {
+ if (_resources.Any())
+ return;
+
+ var assembly = Assembly.GetExecutingAssembly();
+ foreach (var resourceName in assembly.GetManifestResourceNames())
+ {
+ if (!resourceName.StartsWith(_resourcePrefix, StringComparison.Ordinal))
+ continue;
+
+ var relativePath = resourceName.Substring(_resourcePrefix.Length).Replace('\\', '/');
+ if (string.IsNullOrEmpty(relativePath))
+ continue;
+
+ using var stream = assembly.GetManifestResourceStream(resourceName);
+ if (stream == null)
+ continue;
+
+ using var memoryStream = new MemoryStream();
+ stream.CopyTo(memoryStream);
+ _resources[$"/{relativePath}"] = memoryStream.ToArray();
+ }
+ }
+
+ public void PreviewCompiledHtmlHelp(string path)
+ {
+ FallbackPath = Path.GetDirectoryName(path);
+
+ var chmFileUrl = Helper.FilePathToFileUrl(path);
+ var pluginUri = new Uri($"file://quicklook/?plugin=1&chm={Uri.EscapeDataString(chmFileUrl.AbsoluteUri)}");
+
+ NavigateToUri(pluginUri);
+ }
+
+ protected override void WebView_WebResourceRequested(object sender, Microsoft.Web.WebView2.Core.CoreWebView2WebResourceRequestedEventArgs args)
+ {
+ try
+ {
+ if (string.IsNullOrWhiteSpace(args.Request.Uri))
+ {
+ base.WebView_WebResourceRequested(sender, args);
+ return;
+ }
+
+ var requestedUri = new Uri(args.Request.Uri);
+ if (requestedUri.Scheme != "file")
+ {
+ base.WebView_WebResourceRequested(sender, args);
+ return;
+ }
+
+ var requestedPath = Uri.UnescapeDataString(requestedUri.AbsolutePath);
+ if (requestedPath == "/")
+ {
+ var response = _webView.CoreWebView2.Environment.CreateWebResourceResponse(
+ new MemoryStream(_homePage), 200, "OK", MimeTypes.GetContentType(".html"));
+ args.Response = response;
+ return;
+ }
+
+ if (ContainsKey(requestedPath))
+ {
+ var response = _webView.CoreWebView2.Environment.CreateWebResourceResponse(
+ ReadStream(requestedPath), 200, "OK", MimeTypes.GetContentType(Path.GetExtension(requestedPath)));
+ args.Response = response;
+ return;
+ }
+
+ base.WebView_WebResourceRequested(sender, args);
+ }
+ catch
+ {
+ base.WebView_WebResourceRequested(sender, args);
+ }
+ }
+
+ private static bool ContainsKey(string key) => _resources.ContainsKey(key);
+
+ private static Stream ReadStream(string key) => new MemoryStream(_resources[key]);
+
+ private static byte[] ReadResource(string key) => _resources.TryGetValue(key, out var value) ? value : null;
+}
diff --git a/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/Helper.cs b/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/Helper.cs
new file mode 100644
index 0000000..4ed82fc
--- /dev/null
+++ b/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/Helper.cs
@@ -0,0 +1,52 @@
+// Copyright © 2017-2026 QL-Win Contributors
+//
+// This file is part of QuickLook program.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+using System;
+using System.IO;
+using System.Text;
+
+namespace QuickLook.Plugin.ChmViewer;
+
+internal static class Helper
+{
+ public static Uri FilePathToFileUrl(string filePath)
+ {
+ var uri = new StringBuilder();
+ foreach (var v in filePath)
+ if (v >= 'a' && v <= 'z' || v >= 'A' && v <= 'Z' || v >= '0' && v <= '9' ||
+ v == '+' || v == '/' || v == ':' || v == '.' || v == '-' || v == '_' || v == '~' ||
+ v > '\x80')
+ uri.Append(v);
+ else if (v == Path.DirectorySeparatorChar || v == Path.AltDirectorySeparatorChar)
+ uri.Append('/');
+ else
+ uri.Append($"%{(int)v:X2}");
+ if (uri.Length >= 2 && uri[0] == '/' && uri[1] == '/') // UNC path - Universal Naming Convention
+ uri.Insert(0, "file:");
+ else
+ uri.Insert(0, "file:///");
+
+ try
+ {
+ return new Uri(uri.ToString());
+ }
+ catch
+ {
+ return null;
+ }
+ }
+}
diff --git a/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/Plugin.cs b/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/Plugin.cs
index a8d958b..9d725b5 100644
--- a/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/Plugin.cs
+++ b/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/Plugin.cs
@@ -24,7 +24,13 @@ namespace QuickLook.Plugin.ChmViewer;
public sealed class Plugin : IViewer
{
- public int Priority => 0;
+ private ChmWebpagePanel _panel;
+
+ ///
+ /// The implementation of this plugin is better than following
+ /// https://github.com/emako/QuickLook.Plugin.SumatraPDFReader
+ ///
+ public int Priority => 2;
public void Init()
{
@@ -32,26 +38,29 @@ public sealed class Plugin : IViewer
public bool CanHandle(string path)
{
- return false;
-#pragma warning disable CS0162 // Unreachable code detected
return !Directory.Exists(path)
&& Path.GetExtension(path).Equals(".chm", StringComparison.OrdinalIgnoreCase);
-#pragma warning restore CS0162 // Unreachable code detected
}
public void Prepare(string path, ContextObject context)
{
- context.Title = Path.GetFileName(path);
- context.IsBlocked = true;
- context.PreferredSize = new Size { Width = 800, Height = 600 };
+ context.PreferredSize = new Size(1280, 720);
}
public void View(string path, ContextObject context)
{
+ _panel = new ChmWebpagePanel();
+ _panel.PreviewCompiledHtmlHelp(path);
+
+ context.ViewerContent = _panel;
+ context.Title = Path.GetFileName(path);
context.IsBusy = false;
}
public void Cleanup()
{
+ _panel?.Dispose();
+ _panel = null;
+ GC.SuppressFinalize(this);
}
}
diff --git a/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/QuickLook.Plugin.ChmViewer.csproj b/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/QuickLook.Plugin.ChmViewer.csproj
index 4d6aa2f..3fc929f 100644
--- a/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/QuickLook.Plugin.ChmViewer.csproj
+++ b/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/QuickLook.Plugin.ChmViewer.csproj
@@ -76,6 +76,21 @@
QuickLook.Common
False
+
+ {CE22A1F3-7F2C-4EC8-BFDE-B58D0EB625FC}
+ QuickLook.Plugin.HtmlViewer
+ False
+
+
+
+
+
+ all
+
+
+
+
+
diff --git a/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/Resources/chm2html.html b/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/Resources/chm2html.html
new file mode 100644
index 0000000..70666b3
--- /dev/null
+++ b/QuickLook.Plugin/QuickLook.Plugin.ChmViewer/Resources/chm2html.html
@@ -0,0 +1,85 @@
+
+
+
+
+
+ chm2html — CHM Viewer (Plugin)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
📄
+
Drop a .chm file here
+
or
+
+
+
+
+
+
+
+
+
+
+
+