From b0dae69889cf370726bdaed3d757067265f512db Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 26 Nov 2025 21:06:21 +0000
Subject: [PATCH 1/6] Initial plan
From 0d9e80eb36c385228fb290d4d1d1d87a73dc3c9b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 26 Nov 2025 21:12:50 +0000
Subject: [PATCH 2/6] Add ExtensionFilterHelper and integrate with
ViewWindowManager
Co-authored-by: emako <24737061+emako@users.noreply.github.com>
---
QuickLook/ViewWindowManager.cs | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/QuickLook/ViewWindowManager.cs b/QuickLook/ViewWindowManager.cs
index ee1ce2bdd..121873fe8 100644
--- a/QuickLook/ViewWindowManager.cs
+++ b/QuickLook/ViewWindowManager.cs
@@ -155,6 +155,10 @@ public void InvokePreview(string path = null)
if (!path.StartsWith("::")) // CLSID
return;
+ // Check extension filtering before proceeding
+ if (!ExtensionFilterHelper.IsExtensionAllowed(path))
+ return;
+
_invokedPath = path;
RunFocusMonitor();
@@ -175,6 +179,10 @@ public void InvokePluginPreview(string plugin, string path = null)
if (!Directory.Exists(path) && !File.Exists(path))
return;
+ // Check extension filtering before proceeding
+ if (!ExtensionFilterHelper.IsExtensionAllowed(path))
+ return;
+
RunFocusMonitor();
var matchedPlugin = PluginManager.GetInstance().LoadedPlugins.Find(p =>
From 15ec6b47491a596f745f0a1606cc3f90caa76849 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 26 Nov 2025 21:14:29 +0000
Subject: [PATCH 3/6] Add ExtensionFilterHelper to main QuickLook project
Co-authored-by: emako <24737061+emako@users.noreply.github.com>
---
QuickLook/Helpers/ExtensionFilterHelper.cs | 189 +++++++++++++++++++++
1 file changed, 189 insertions(+)
create mode 100644 QuickLook/Helpers/ExtensionFilterHelper.cs
diff --git a/QuickLook/Helpers/ExtensionFilterHelper.cs b/QuickLook/Helpers/ExtensionFilterHelper.cs
new file mode 100644
index 000000000..4956f37a2
--- /dev/null
+++ b/QuickLook/Helpers/ExtensionFilterHelper.cs
@@ -0,0 +1,189 @@
+// Copyright © 2017-2025 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.Common.Helpers;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace QuickLook.Helpers;
+
+///
+/// Helper class for managing file extension allowlist/blocklist filtering.
+/// When allowlist mode is enabled, only extensions in the allowlist can be previewed.
+/// When blocklist mode is enabled (default), extensions in the blocklist are blocked from preview.
+///
+public static class ExtensionFilterHelper
+{
+ private const string AllowlistKey = "ExtensionAllowlist";
+ private const string BlocklistKey = "ExtensionBlocklist";
+ private const string UseAllowlistModeKey = "UseExtensionAllowlistMode";
+
+ private static HashSet _allowlistCache;
+ private static HashSet _blocklistCache;
+ private static bool? _useAllowlistModeCache;
+
+ ///
+ /// Gets or sets whether to use allowlist mode.
+ /// When true, only extensions in the allowlist can be previewed.
+ /// When false (default), extensions in the blocklist are blocked from preview.
+ ///
+ public static bool UseAllowlistMode
+ {
+ get
+ {
+ _useAllowlistModeCache ??= SettingHelper.Get(UseAllowlistModeKey, false);
+ return _useAllowlistModeCache.Value;
+ }
+ set
+ {
+ _useAllowlistModeCache = value;
+ SettingHelper.Set(UseAllowlistModeKey, value);
+ }
+ }
+
+ ///
+ /// Gets the current allowlist of file extensions.
+ /// Extensions should be in the format ".ext" (with leading dot).
+ ///
+ public static HashSet Allowlist
+ {
+ get
+ {
+ if (_allowlistCache == null)
+ {
+ var list = SettingHelper.Get(AllowlistKey, string.Empty);
+ _allowlistCache = ParseExtensionList(list);
+ }
+ return _allowlistCache;
+ }
+ }
+
+ ///
+ /// Gets the current blocklist of file extensions.
+ /// Extensions should be in the format ".ext" (with leading dot).
+ ///
+ public static HashSet Blocklist
+ {
+ get
+ {
+ if (_blocklistCache == null)
+ {
+ var list = SettingHelper.Get(BlocklistKey, string.Empty);
+ _blocklistCache = ParseExtensionList(list);
+ }
+ return _blocklistCache;
+ }
+ }
+
+ ///
+ /// Sets the allowlist of file extensions.
+ ///
+ /// Collection of extensions in the format ".ext" (with leading dot).
+ public static void SetAllowlist(IEnumerable extensions)
+ {
+ var normalized = NormalizeExtensions(extensions);
+ _allowlistCache = normalized;
+ SettingHelper.Set(AllowlistKey, string.Join(";", normalized));
+ }
+
+ ///
+ /// Sets the blocklist of file extensions.
+ ///
+ /// Collection of extensions in the format ".ext" (with leading dot).
+ public static void SetBlocklist(IEnumerable extensions)
+ {
+ var normalized = NormalizeExtensions(extensions);
+ _blocklistCache = normalized;
+ SettingHelper.Set(BlocklistKey, string.Join(";", normalized));
+ }
+
+ ///
+ /// Checks if a file path is allowed for preview based on the current filter settings.
+ ///
+ /// The file path to check.
+ /// True if the file is allowed for preview, false if it should be blocked.
+ public static bool IsExtensionAllowed(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ return true;
+
+ // Directories are always allowed (extension filtering only applies to files)
+ if (Directory.Exists(path))
+ return true;
+
+ var extension = Path.GetExtension(path);
+ if (string.IsNullOrEmpty(extension))
+ return true;
+
+ extension = extension.ToLowerInvariant();
+
+ if (UseAllowlistMode)
+ {
+ // In allowlist mode: only allow if extension is in the allowlist
+ // If allowlist is empty, allow all (no filtering)
+ return Allowlist.Count == 0 || Allowlist.Contains(extension);
+ }
+ else
+ {
+ // In blocklist mode: block if extension is in the blocklist
+ return !Blocklist.Contains(extension);
+ }
+ }
+
+ ///
+ /// Clears the cached settings, forcing a reload from the config file.
+ ///
+ public static void ClearCache()
+ {
+ _allowlistCache = null;
+ _blocklistCache = null;
+ _useAllowlistModeCache = null;
+ }
+
+ private static HashSet ParseExtensionList(string list)
+ {
+ if (string.IsNullOrWhiteSpace(list))
+ return new HashSet(StringComparer.OrdinalIgnoreCase);
+
+ return new HashSet(
+ list.Split(new[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries)
+ .Select(NormalizeExtension)
+ .Where(e => !string.IsNullOrEmpty(e)),
+ StringComparer.OrdinalIgnoreCase);
+ }
+
+ private static HashSet NormalizeExtensions(IEnumerable extensions)
+ {
+ return new HashSet(
+ extensions.Select(NormalizeExtension).Where(e => !string.IsNullOrEmpty(e)),
+ StringComparer.OrdinalIgnoreCase);
+ }
+
+ private static string NormalizeExtension(string ext)
+ {
+ if (string.IsNullOrWhiteSpace(ext))
+ return null;
+
+ ext = ext.Trim().ToLowerInvariant();
+ if (!ext.StartsWith("."))
+ ext = "." + ext;
+
+ return ext;
+ }
+}
From f54f037b01e075faf7d8489d5cb7c97bf6ff1473 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 26 Nov 2025 21:16:22 +0000
Subject: [PATCH 4/6] Address code review feedback: improve docs and remove
redundant I/O
Co-authored-by: emako <24737061+emako@users.noreply.github.com>
---
QuickLook/Helpers/ExtensionFilterHelper.cs | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/QuickLook/Helpers/ExtensionFilterHelper.cs b/QuickLook/Helpers/ExtensionFilterHelper.cs
index 4956f37a2..579c360db 100644
--- a/QuickLook/Helpers/ExtensionFilterHelper.cs
+++ b/QuickLook/Helpers/ExtensionFilterHelper.cs
@@ -25,8 +25,17 @@ namespace QuickLook.Helpers;
///
/// Helper class for managing file extension allowlist/blocklist filtering.
-/// When allowlist mode is enabled, only extensions in the allowlist can be previewed.
-/// When blocklist mode is enabled (default), extensions in the blocklist are blocked from preview.
+///
+/// Blocklist mode (default): All extensions are allowed except those in the blocklist.
+/// If the blocklist is empty, all files are allowed.
+///
+///
+/// Allowlist mode: Only extensions in the allowlist can be previewed.
+/// If the allowlist is empty in allowlist mode, all files are allowed (no filtering).
+///
+///
+/// Directories and files without extensions are always allowed regardless of the mode.
+///
///
public static class ExtensionFilterHelper
{
@@ -123,11 +132,9 @@ public static bool IsExtensionAllowed(string path)
if (string.IsNullOrEmpty(path))
return true;
- // Directories are always allowed (extension filtering only applies to files)
- if (Directory.Exists(path))
- return true;
-
var extension = Path.GetExtension(path);
+
+ // Files without extensions are always allowed (includes directories)
if (string.IsNullOrEmpty(extension))
return true;
From eade320211f3eb17f06c1dcf37efdb3af349ec1a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 26 Nov 2025 21:21:36 +0000
Subject: [PATCH 5/6] Address code review: optimize directory checks and use
static separator array
Co-authored-by: emako <24737061+emako@users.noreply.github.com>
---
QuickLook/Helpers/ExtensionFilterHelper.cs | 3 ++-
QuickLook/ViewWindowManager.cs | 14 ++++++++------
2 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/QuickLook/Helpers/ExtensionFilterHelper.cs b/QuickLook/Helpers/ExtensionFilterHelper.cs
index 579c360db..32d6f0ec4 100644
--- a/QuickLook/Helpers/ExtensionFilterHelper.cs
+++ b/QuickLook/Helpers/ExtensionFilterHelper.cs
@@ -42,6 +42,7 @@ public static class ExtensionFilterHelper
private const string AllowlistKey = "ExtensionAllowlist";
private const string BlocklistKey = "ExtensionBlocklist";
private const string UseAllowlistModeKey = "UseExtensionAllowlistMode";
+ private static readonly char[] ExtensionSeparators = [';', ','];
private static HashSet _allowlistCache;
private static HashSet _blocklistCache;
@@ -169,7 +170,7 @@ private static HashSet ParseExtensionList(string list)
return new HashSet(StringComparer.OrdinalIgnoreCase);
return new HashSet(
- list.Split(new[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries)
+ list.Split(ExtensionSeparators, StringSplitOptions.RemoveEmptyEntries)
.Select(NormalizeExtension)
.Where(e => !string.IsNullOrEmpty(e)),
StringComparer.OrdinalIgnoreCase);
diff --git a/QuickLook/ViewWindowManager.cs b/QuickLook/ViewWindowManager.cs
index 121873fe8..563ead67b 100644
--- a/QuickLook/ViewWindowManager.cs
+++ b/QuickLook/ViewWindowManager.cs
@@ -151,12 +151,13 @@ public void InvokePreview(string path = null)
if (_viewerWindow.IsVisible && path == _invokedPath)
return;
- if (!Directory.Exists(path) && !File.Exists(path))
+ var isDirectory = Directory.Exists(path);
+ if (!isDirectory && !File.Exists(path))
if (!path.StartsWith("::")) // CLSID
return;
- // Check extension filtering before proceeding
- if (!ExtensionFilterHelper.IsExtensionAllowed(path))
+ // Check extension filtering before proceeding (skip for directories)
+ if (!isDirectory && !ExtensionFilterHelper.IsExtensionAllowed(path))
return;
_invokedPath = path;
@@ -176,11 +177,12 @@ public void InvokePluginPreview(string plugin, string path = null)
if (string.IsNullOrEmpty(path))
return;
- if (!Directory.Exists(path) && !File.Exists(path))
+ var isDirectory = Directory.Exists(path);
+ if (!isDirectory && !File.Exists(path))
return;
- // Check extension filtering before proceeding
- if (!ExtensionFilterHelper.IsExtensionAllowed(path))
+ // Check extension filtering before proceeding (skip for directories)
+ if (!isDirectory && !ExtensionFilterHelper.IsExtensionAllowed(path))
return;
RunFocusMonitor();
From d6de335da5d91f21d8bf40cac9b50ff6619719b2 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 26 Nov 2025 21:23:11 +0000
Subject: [PATCH 6/6] Add trailing newline to ExtensionFilterHelper.cs
Co-authored-by: emako <24737061+emako@users.noreply.github.com>
---
QuickLook/Helpers/ExtensionFilterHelper.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/QuickLook/Helpers/ExtensionFilterHelper.cs b/QuickLook/Helpers/ExtensionFilterHelper.cs
index 32d6f0ec4..0027e2919 100644
--- a/QuickLook/Helpers/ExtensionFilterHelper.cs
+++ b/QuickLook/Helpers/ExtensionFilterHelper.cs
@@ -195,3 +195,4 @@ private static string NormalizeExtension(string ext)
return ext;
}
}
+