Repository: oliverschwendener/ueli
Pull request #1317 (2025-01-13 14:57:02)

Add rescan indicator
The goal of this PR is to add a rescan indiacator in the search window inspired by the proposal of @tkohlmeier in #1308.

Sorry for rejecting your proposal again, I don't want to dishonor your time and efforts, but I saw a few things that could be optimized:
- `RescanCounter` could be removed if we have a rescan state manager
- Having the state of the rescan status controlled by a separate react hook on the UI
- Having the state of the rescan status managed outside of the `ExtensionManager` class as that is already bloated and doing too much
- Having `try/finally` only where really needed
- Having an error message when triggering a manual rescan for a single extension

Feel free to leave your thoughts.

tkohlmeier: No worries, your solution looks much better. Just found some minor issues.
oliverschwendener: >Just found some minor issues.

Yes? :)
tkohlmeier: Oh, I forgot to click a button... Now you should see my comments.

Patch:
diff --git a/src/common/Core/ContextBridge.ts b/src/common/Core/ContextBridge.ts
index 86bcd344..948a0b1e 100644
--- a/src/common/Core/ContextBridge.ts
+++ b/src/common/Core/ContextBridge.ts
@@ -3,6 +3,7 @@ import type { AboutUeli } from "./AboutUeli";
 import type { ExtensionInfo } from "./ExtensionInfo";
 import type { InstantSearchResultItems } from "./InstantSearchResultItems";
 import type { OperatingSystem } from "./OperatingSystem";
+import type { RescanStatus } from "./RescanStatus";
 import type { SearchResultItem } from "./SearchResultItem";
 import type { SearchResultItemAction } from "./SearchResultItemAction";
 import type { Terminal } from "./Terminal";
@@ -38,9 +39,9 @@ export type ContextBridge = {
     getInstantSearchResultItems: (searchTerm: string) => InstantSearchResultItems;
     getLogs: () => string[];
     getOperatingSystem: () => OperatingSystem;
-    getScanCount: () => number;
     getSearchResultItems: () => SearchResultItem[];
     getSettingValue: <Value>(key: string, defaultValue: Value, isSensitive?: boolean) => Value;
+    getRescanStatus: () => RescanStatus;
     invokeAction: (action: SearchResultItemAction) => Promise<void>;
     invokeExtension: <Argument, Result>(extensionId: string, searchArguments: Argument) => Promise<Result>;
     openExternal: (url: string, options?: OpenExternalOptions) => Promise<void>;
diff --git a/src/common/Core/RescanStatus.ts b/src/common/Core/RescanStatus.ts
new file mode 100644
index 00000000..53f07ba3
--- /dev/null
+++ b/src/common/Core/RescanStatus.ts
@@ -0,0 +1,1 @@
+export type RescanStatus = "idle" | "scanning";
diff --git a/src/common/Core/index.ts b/src/common/Core/index.ts
index 57e14c85..12e07c83 100644
--- a/src/common/Core/index.ts
+++ b/src/common/Core/index.ts
@@ -7,6 +7,7 @@ export * from "./FluentIcon";
 export * from "./InstantSearchResultItems";
 export * from "./NavigateToArgument";
 export * from "./OperatingSystem";
+export * from "./RescanStatus";
 export * from "./SearchResultItem";
 export * from "./SearchResultItemAction";
 export * from "./SearchResultItemActionUtility";
diff --git a/src/main/Core/ExtensionManager/ExtensionManager.ts b/src/main/Core/ExtensionManager/ExtensionManager.ts
index 24123422..e1c6bdf9 100644
--- a/src/main/Core/ExtensionManager/ExtensionManager.ts
+++ b/src/main/Core/ExtensionManager/ExtensionManager.ts
@@ -3,7 +3,6 @@ import type { Logger } from "@Core/Logger";
 import type { Index, SearchIndex } from "@Core/SearchIndex";
 import type { SettingsManager } from "@Core/SettingsManager";
 import { createEmptyInstantSearchResult, type InstantSearchResultItems } from "@common/Core";
-import type { ScanCounter } from "./ScanCounter";
 
 export class ExtensionManager {
     public constructor(
@@ -11,7 +10,6 @@ export class ExtensionManager {
         private readonly searchIndex: SearchIndex,
         private readonly settingsManager: SettingsManager,
         private readonly logger: Logger,
-        private readonly scanCounter: ScanCounter,
     ) {}
 
     public async populateSearchIndex() {
@@ -38,15 +36,12 @@ export class ExtensionManager {
         }
 
         this.searchIndex.set(newIndex);
-        this.scanCounter.increment();
     }
 
     public async populateSearchIndexByExtensionId(extensionId: string) {
         const extension = this.extensionRegistry.getById(extensionId);
         const searchResultItems = await extension.getSearchResultItems();
         this.searchIndex.addSearchResultItems(extension.id, searchResultItems);
-
-        this.scanCounter.increment();
     }
 
     public getSupportedExtensions() {
diff --git a/src/main/Core/ExtensionManager/ExtensionManagerModule.ts b/src/main/Core/ExtensionManager/ExtensionManagerModule.ts
index d853717c..cc96086f 100644
--- a/src/main/Core/ExtensionManager/ExtensionManagerModule.ts
+++ b/src/main/Core/ExtensionManager/ExtensionManagerModule.ts
@@ -1,16 +1,8 @@
 import type { Extension } from "@Core/Extension";
 import type { UeliModuleRegistry } from "@Core/ModuleRegistry";
-import type { ExtensionInfo } from "@common/Core";
 import { ExtensionManager } from "./ExtensionManager";
-import { ScanCounter } from "./ScanCounter";
-
-const mapExtensionToInfo = (extension: Extension): ExtensionInfo => ({
-    id: extension.id,
-    name: extension.name,
-    nameTranslation: extension.nameTranslation,
-    image: extension.getImage(),
-    author: extension.author,
-});
+import { RescanStatusManager } from "./RescanStatusManager";
+import { mapExtensionToInfo } from "./mapExtensionToInfo";
 
 export class ExtensionManagerModule {
     public static async bootstrap(moduleRegistry: UeliModuleRegistry) {
@@ -22,17 +14,11 @@ export class ExtensionManagerModule {
         const searchIndex = moduleRegistry.get("SearchIndex");
         const settingsManager = moduleRegistry.get("SettingsManager");
 
-        const scanCounter = new ScanCounter();
+        const rescanStatusManager = new RescanStatusManager("idle", browserWindowNotifier);
 
-        const extensionManager = new ExtensionManager(
-            extensionRegistry,
-            searchIndex,
-            settingsManager,
-            logger,
-            scanCounter,
-        );
+        const extensionManager = new ExtensionManager(extensionRegistry, searchIndex, settingsManager, logger);
 
-        ipcMain.on("getScanCount", (event) => (event.returnValue = scanCounter.getScanCount()));
+        ipcMain.on("getRescanStatus", (event) => (event.returnValue = rescanStatusManager.get()));
 
         ipcMain.on("getExtensionResources", (event) => {
             event.returnValue = extensionManager.getSupportedExtensions().map((extension) => ({
@@ -46,8 +32,14 @@ export class ExtensionManagerModule {
         });
 
         ipcMain.on("extensionEnabled", async (_, { extensionId }: { extensionId: string }) => {
-            await extensionManager.populateSearchIndexByExtensionId(extensionId);
-            browserWindowNotifier.notifyAll({ channel: "extensionEnabled", data: { extensionId } });
+            rescanStatusManager.change("scanning");
+
+            try {
+                await extensionManager.populateSearchIndexByExtensionId(extensionId);
+                browserWindowNotifier.notifyAll({ channel: "extensionEnabled", data: { extensionId } });
+            } finally {
+                rescanStatusManager.change("idle");
+            }
         });
 
         ipcMain.on("extensionDisabled", async (_, { extensionId }: { extensionId: string }) => {
@@ -91,25 +83,36 @@ export class ExtensionManagerModule {
             },
         );
 
-        ipcMain.handle("triggerExtensionRescan", (_, { extensionId }: { extensionId: string }) =>
-            extensionManager.populateSearchIndexByExtensionId(extensionId),
-        );
+        ipcMain.handle("triggerExtensionRescan", async (_, { extensionId }: { extensionId: string }) => {
+            rescanStatusManager.change("scanning");
 
-        eventSubscriber.subscribe(
-            "RescanOrchestrator:timeElapsed",
-            async () => await extensionManager.populateSearchIndex(),
-        );
+            try {
+                await extensionManager.populateSearchIndexByExtensionId(extensionId);
+            } finally {
+                rescanStatusManager.change("idle");
+            }
+        });
+
+        eventSubscriber.subscribe("RescanOrchestrator:timeElapsed", async () => {
+            rescanStatusManager.change("scanning");
+            await extensionManager.populateSearchIndex();
+            rescanStatusManager.change("idle");
+        });
 
         eventSubscriber.subscribe("settingUpdated", async ({ key }: { key: string }) => {
             const extensionNeedsRescan = (extension: Extension) =>
                 extension.getSettingKeysTriggeringRescan && extension.getSettingKeysTriggeringRescan().includes(key);
 
+            rescanStatusManager.change("scanning");
+
             await Promise.allSettled(
                 extensionManager
                     .getSupportedExtensions()
                     .filter((extension) => extensionNeedsRescan(extension))
                     .map((extension) => extensionManager.populateSearchIndexByExtensionId(extension.id)),
             );
+
+            rescanStatusManager.change("idle");
         });
     }
 }
diff --git a/src/main/Core/ExtensionManager/RescanStatusManager.ts b/src/main/Core/ExtensionManager/RescanStatusManager.ts
new file mode 100644
index 00000000..b2377f2c
--- /dev/null
+++ b/src/main/Core/ExtensionManager/RescanStatusManager.ts
@@ -0,0 +1,18 @@
+import type { RescanStatus } from "@common/Core";
+import type { BrowserWindowNotifier } from "@Core/BrowserWindowNotifier";
+
+export class RescanStatusManager {
+    public constructor(
+        private status: RescanStatus,
+        private readonly browserWindowNotifier: BrowserWindowNotifier,
+    ) {}
+
+    public change(status: RescanStatus): void {
+        this.status = status;
+        this.browserWindowNotifier.notifyAll({ channel: "rescanStatusChanged", data: { status } });
+    }
+
+    public get(): RescanStatus {
+        return this.status;
+    }
+}
diff --git a/src/main/Core/ExtensionManager/ScanCounter.ts b/src/main/Core/ExtensionManager/ScanCounter.ts
deleted file mode 100644
index f62eb9cc..00000000
--- a/src/main/Core/ExtensionManager/ScanCounter.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-export class ScanCounter {
-    public constructor(private scanCount = 0) {}
-
-    public increment() {
-        this.scanCount++;
-    }
-
-    public getScanCount() {
-        return this.scanCount;
-    }
-}
diff --git a/src/main/Core/ExtensionManager/mapExtensionToInfo.ts b/src/main/Core/ExtensionManager/mapExtensionToInfo.ts
new file mode 100644
index 00000000..d3d4a9d7
--- /dev/null
+++ b/src/main/Core/ExtensionManager/mapExtensionToInfo.ts
@@ -0,0 +1,10 @@
+import type { ExtensionInfo } from "@common/Core";
+import type { Extension } from "@Core/Extension";
+
+export const mapExtensionToInfo = (extension: Extension): ExtensionInfo => ({
+    id: extension.id,
+    name: extension.name,
+    nameTranslation: extension.nameTranslation,
+    image: extension.getImage(),
+    author: extension.author,
+});
diff --git a/src/preload/index.ts b/src/preload/index.ts
index d0609f91..04414cd3 100644
--- a/src/preload/index.ts
+++ b/src/preload/index.ts
@@ -36,7 +36,7 @@ const contextBridgeImplementation: ContextBridge = {
     getSearchResultItems: () => ipcRenderer.sendSync("getSearchResultItems"),
     getSettingValue: (key, defaultValue, isSensitive) =>
         ipcRenderer.sendSync("getSettingValue", { key, defaultValue, isSensitive }),
-    getScanCount: () => ipcRenderer.sendSync("getScanCount"),
+    getRescanStatus: () => ipcRenderer.sendSync("getRescanStatus"),
     invokeAction: (action) => ipcRenderer.invoke("invokeAction", { action }),
     invokeExtension: (extensionId, argument) => ipcRenderer.invoke("invokeExtension", { extensionId, argument }),
     openExternal: (url, options) => ipcRenderer.invoke("openExternal", { url, options }),
diff --git a/src/renderer/Core/Hooks/index.ts b/src/renderer/Core/Hooks/index.ts
index d84904e9..a301e4bb 100644
--- a/src/renderer/Core/Hooks/index.ts
+++ b/src/renderer/Core/Hooks/index.ts
@@ -2,6 +2,7 @@ export * from "./useExcludedSearchResultItems";
 export * from "./useExtensionProps";
 export * from "./useExtensionSetting";
 export * from "./useFavorites";
+export * from "./useRescanStatus";
 export * from "./useScrollbar";
 export * from "./useSearchResultItems";
 export * from "./useSetting";
diff --git a/src/renderer/Core/Hooks/useRescanStatus.ts b/src/renderer/Core/Hooks/useRescanStatus.ts
new file mode 100644
index 00000000..21a29379
--- /dev/null
+++ b/src/renderer/Core/Hooks/useRescanStatus.ts
@@ -0,0 +1,21 @@
+import type { RescanStatus } from "@common/Core";
+import type { IpcRendererEvent } from "electron";
+import { useEffect, useState } from "react";
+
+export const useRescanStatus = () => {
+    const [status, setStatus] = useState<RescanStatus>(window.ContextBridge.getRescanStatus());
+
+    useEffect(() => {
+        const statusChange = (_: IpcRendererEvent, { status: newStatus }: { status: RescanStatus }) => {
+            setStatus(newStatus);
+        };
+
+        window.ContextBridge.ipcRenderer.on("rescanStatusChanged", statusChange);
+
+        return () => {
+            window.ContextBridge.ipcRenderer.off("rescanStatusChanged", statusChange);
+        };
+    }, []);
+
+    return { status };
+};
diff --git a/src/renderer/Core/I18n/getCoreResources.ts b/src/renderer/Core/I18n/getCoreResources.ts
index e2640bf8..6c444f9e 100644
--- a/src/renderer/Core/I18n/getCoreResources.ts
+++ b/src/renderer/Core/I18n/getCoreResources.ts
@@ -11,6 +11,7 @@ export const getCoreResources = (): { namespace: string; resources: Resources<Tr
                     extensionSettings: "Extension settings",
                     actions: "Actions",
                     copiedToClipboard: "Copied",
+                    rescanning: "Rescanning...",
                 },
                 "de-CH": {
                     settings: "Einstellungen",
@@ -18,6 +19,7 @@ export const getCoreResources = (): { namespace: string; resources: Resources<Tr
                     extensionSettings: "Erweiterungseinstellungen",
                     actions: "Aktionen",
                     copiedToClipboard: "Kopiert",
+                    rescanning: "Scanne neu...",
                 },
             },
         },
@@ -208,6 +210,7 @@ export const getCoreResources = (): { namespace: string; resources: Resources<Tr
                     enabled: "Enabled",
                     rescan: "Rescan",
                     successfulRescan: "Successfully rescanned",
+                    failedRescan: "Failed to rescan. Check logs for more information.",
                     noSettingsAvailable: "No settings available",
                 },
                 "de-CH": {
@@ -217,6 +220,7 @@ export const getCoreResources = (): { namespace: string; resources: Resources<Tr
                     enabled: "Aktiviert",
                     rescan: "Neuscan",
                     successfulRescan: "Erfolgreicher Neuscan",
+                    failedRescan: "Fehler beim neu scannen. Überprüfe die Logs für mehr Informationen.",
                     noSettingsAvailable: "Keine Einstellungen verfügbar",
                 },
             },
diff --git a/src/renderer/Core/Search/RescanIndicator.tsx b/src/renderer/Core/Search/RescanIndicator.tsx
new file mode 100644
index 00000000..3cdee6a3
--- /dev/null
+++ b/src/renderer/Core/Search/RescanIndicator.tsx
@@ -0,0 +1,13 @@
+import { Caption1, Spinner } from "@fluentui/react-components";
+import { useTranslation } from "react-i18next";
+
+export const RescanIndicator = () => {
+    const { t } = useTranslation("general");
+
+    return (
+        <div style={{ display: "flex", flexDirection: "row", alignItems: "center", gap: 8 }}>
+            <Spinner size="extra-tiny" />
+            <Caption1>{t("rescanning")}</Caption1>
+        </div>
+    );
+};
diff --git a/src/renderer/Core/Search/Search.tsx b/src/renderer/Core/Search/Search.tsx
index 16ddc82b..697428f4 100644
--- a/src/renderer/Core/Search/Search.tsx
+++ b/src/renderer/Core/Search/Search.tsx
@@ -1,5 +1,5 @@
 import { KeyboardShortcut } from "@Core/Components";
-import { useSetting } from "@Core/Hooks";
+import { useRescanStatus, useSetting } from "@Core/Hooks";
 import type { OperatingSystem, SearchResultItem } from "@common/Core";
 import { Button, Divider, Text, tokens, Tooltip } from "@fluentui/react-components";
 import { ArrowEnterLeftFilled, Settings16Regular } from "@fluentui/react-icons";
@@ -10,7 +10,7 @@ import { Footer } from "../Footer";
 import { ActionsMenu } from "./ActionsMenu";
 import { ConfirmationDialog } from "./ConfirmationDialog";
 import type { KeyboardEventHandler } from "./KeyboardEventHandler";
-import { ScanIndicator } from "./ScanIndicator";
+import { RescanIndicator } from "./RescanIndicator";
 import { SearchBar } from "./SearchBar";
 import type { SearchBarAppearance } from "./SearchBarAppearance";
 import type { SearchBarSize } from "./SearchBarSize";
@@ -238,6 +238,8 @@ export const Search = ({
         defaultValue: true,
     });
 
+    const { status: rescanStatus } = useRescanStatus();
+
     return (
         <BaseLayout
             header={
@@ -277,49 +279,45 @@ export const Search = ({
             }
             contentRef={containerRef}
             content={
-                window.ContextBridge.getScanCount() === 0 && !window.ContextBridge.searchIndexCacheFileExists() ? (
-                    <ScanIndicator />
-                ) : (
-                    <>
-                        <ConfirmationDialog
-                            closeDialog={closeConfirmationDialog}
-                            action={confirmationDialog.action.value}
-                        />
-                        <div
-                            style={{
-                                display: "flex",
-                                flexDirection: "column",
-                                gap: 10,
-                                padding: 10,
-                                boxSizing: "border-box",
-                            }}
-                        >
-                            {Object.keys(searchResult.value)
-                                .filter((group) => searchResult.value[group].length)
-                                .map((group) => (
-                                    <div key={`search-result-group-${group}`}>
-                                        <div style={{ paddingBottom: 5, paddingLeft: 5 }}>
-                                            <Text
-                                                size={200}
-                                                weight="medium"
-                                                style={{ color: tokens.colorNeutralForeground4 }}
-                                            >
-                                                {t(`searchResultGroup.${group}`)}
-                                            </Text>
-                                        </div>
-                                        <SearchResultList
-                                            containerRef={containerRef}
-                                            selectedItemId={selectedItemId.value}
-                                            searchResultItems={searchResult.value[group]}
-                                            searchTerm={searchTerm.value}
-                                            onSearchResultItemClick={handleSearchResultItemClickEvent}
-                                            onSearchResultItemDoubleClick={handleSearchResultItemDoubleClickEvent}
-                                        />
+                <>
+                    <ConfirmationDialog
+                        closeDialog={closeConfirmationDialog}
+                        action={confirmationDialog.action.value}
+                    />
+                    <div
+                        style={{
+                            display: "flex",
+                            flexDirection: "column",
+                            gap: 10,
+                            padding: 10,
+                            boxSizing: "border-box",
+                        }}
+                    >
+                        {Object.keys(searchResult.value)
+                            .filter((group) => searchResult.value[group].length)
+                            .map((group) => (
+                                <div key={`search-result-group-${group}`}>
+                                    <div style={{ paddingBottom: 5, paddingLeft: 5 }}>
+                                        <Text
+                                            size={200}
+                                            weight="medium"
+                                            style={{ color: tokens.colorNeutralForeground4 }}
+                                        >
+                                            {t(`searchResultGroup.${group}`)}
+                                        </Text>
                                     </div>
-                                ))}
-                        </div>
-                    </>
-                )
+                                    <SearchResultList
+                                        containerRef={containerRef}
+                                        selectedItemId={selectedItemId.value}
+                                        searchResultItems={searchResult.value[group]}
+                                        searchTerm={searchTerm.value}
+                                        onSearchResultItemClick={handleSearchResultItemClickEvent}
+                                        onSearchResultItemDoubleClick={handleSearchResultItemDoubleClickEvent}
+                                    />
+                                </div>
+                            ))}
+                    </div>
+                </>
             }
             footer={
                 <Footer draggable>
@@ -343,6 +341,12 @@ export const Search = ({
                                 icon={<Settings16Regular />}
                             />
                         </Tooltip>
+                        {rescanStatus === "scanning" && (
+                            <>
+                                <Divider appearance="subtle" vertical />
+                                <RescanIndicator />
+                            </>
+                        )}
                     </div>
                     <div style={{ display: "flex", flexDirection: "row", gap: 8 }}>
                         {searchResult.current() ? (
diff --git a/src/renderer/Core/Settings/Pages/Extensions.tsx b/src/renderer/Core/Settings/Pages/Extensions.tsx
index 4e730d06..0a478ecc 100644
--- a/src/renderer/Core/Settings/Pages/Extensions.tsx
+++ b/src/renderer/Core/Settings/Pages/Extensions.tsx
@@ -46,19 +46,31 @@ export const Extensions = () => {
         window.ContextBridge.extensionDisabled(extensionId);
     };
 
+    const attemptRescan = async (extensionId: string): Promise<{ success: boolean }> => {
+        try {
+            await window.ContextBridge.triggerExtensionRescan(extensionId);
+            return { success: true };
+        } catch (error) {
+            return { success: false };
+        }
+    };
+
     const triggerExtensionRescan = async (event: MouseEvent, extensionId: string) => {
         event.preventDefault();
-        await window.ContextBridge.triggerExtensionRescan(extensionId);
+
+        const { success } = await attemptRescan(extensionId);
         const { name, nameTranslation } = window.ContextBridge.getExtension(extensionId);
 
         dispatchToast(
             <Toast>
                 <ToastTitle>
                     {nameTranslation ? t(nameTranslation.key, { ns: nameTranslation.namespace }) : name}:{" "}
-                    {t("successfulRescan", { ns: "settingsExtensions" })}
+                    {success
+                        ? t("successfulRescan", { ns: "settingsExtensions" })
+                        : t("failedRescan", { ns: "settingsExtensions" })}
                 </ToastTitle>
             </Toast>,
-            { intent: "success", position: "bottom" },
+            { intent: success ? "success" : "error", position: "bottom" },
         );
     };
 


Test patch:
diff --git a/src/main/Core/ExtensionManager/ExtensionManager.test.ts b/src/main/Core/ExtensionManager/ExtensionManager.test.ts
index 017adf2c..ec67d18f 100644
--- a/src/main/Core/ExtensionManager/ExtensionManager.test.ts
+++ b/src/main/Core/ExtensionManager/ExtensionManager.test.ts
@@ -6,7 +6,6 @@ import type { SettingsManager } from "@Core/SettingsManager";
 import type { InstantSearchResultItems, SearchResultItem } from "@common/Core";
 import { describe, expect, it, vi } from "vitest";
 import { ExtensionManager } from "./ExtensionManager";
-import type { ScanCounter } from "./ScanCounter";
 
 describe(ExtensionManager, () => {
     it("should populate search index by extension id", async () => {
@@ -31,22 +30,12 @@ describe(ExtensionManager, () => {
                 addSearchResultItemsMock(extensionId, searchResultItems),
         };
 
-        const incrementMock = vi.fn();
-        const scanCounter = <ScanCounter>{ increment: () => incrementMock() };
-
-        const extensionManager = new ExtensionManager(
-            extensionRegistry,
-            searchIndex,
-            <SettingsManager>{},
-            <Logger>{},
-            scanCounter,
-        );
+        const extensionManager = new ExtensionManager(extensionRegistry, searchIndex, <SettingsManager>{}, <Logger>{});
 
         await extensionManager.populateSearchIndexByExtensionId(extension.id);
 
         expect(getExtensionByIdMock).toHaveBeenCalledWith(extension.id);
         expect(addSearchResultItemsMock).toHaveBeenCalledWith(extension.id, searchResultItems);
-        expect(incrementMock).toHaveBeenCalledOnce();
     });
 
     it("should get the supported extensions", () => {
@@ -61,7 +50,6 @@ describe(ExtensionManager, () => {
             <SearchIndex>{},
             <SettingsManager>{},
             <Logger>{},
-            <ScanCounter>{},
         );
 
         expect(extensionManager.getSupportedExtensions()).toEqual([extension1]);
@@ -76,13 +64,7 @@ describe(ExtensionManager, () => {
         const getValueMock = vi.fn().mockReturnValue(["extension1"]);
         const settingsManager = <SettingsManager>{ getValue: (key, defaultValue) => getValueMock(key, defaultValue) };
 
-        const extensionManager = new ExtensionManager(
-            extensionRegistry,
-            <SearchIndex>{},
-            settingsManager,
-            <Logger>{},
-            <ScanCounter>{},
-        );
+        const extensionManager = new ExtensionManager(extensionRegistry, <SearchIndex>{}, settingsManager, <Logger>{});
 
         expect(extensionManager.getEnabledExtensions()).toEqual([extension1]);
     });
@@ -115,13 +97,7 @@ describe(ExtensionManager, () => {
         const getValueMock = vi.fn().mockReturnValue([extension1.id, extension2.id]);
         const settingsManager = <SettingsManager>{ getValue: (key, defaultValue) => getValueMock(key, defaultValue) };
 
-        const extensionManager = new ExtensionManager(
-            extensionRegistry,
-            <SearchIndex>{},
-            settingsManager,
-            <Logger>{},
-            <ScanCounter>{},
-        );
+        const extensionManager = new ExtensionManager(extensionRegistry, <SearchIndex>{}, settingsManager, <Logger>{});
 
         const { before, after } = extensionManager.getInstantSearchResultItems("search term");
 
diff --git a/src/main/Core/ExtensionManager/ScanCounter.test.ts b/src/main/Core/ExtensionManager/ScanCounter.test.ts
deleted file mode 100644
index 55c9ba2f..00000000
--- a/src/main/Core/ExtensionManager/ScanCounter.test.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { describe, expect, it } from "vitest";
-import { ScanCounter } from "./ScanCounter";
-
-describe(ScanCounter, () => {
-    describe(ScanCounter.prototype.increment, () => {
-        it("should increment the scan count", () => {
-            const scanCounter = new ScanCounter(17);
-            scanCounter.increment();
-            scanCounter.increment();
-            scanCounter.increment();
-            expect(scanCounter["scanCount"]).toBe(20);
-        });
-    });
-
-    describe(ScanCounter.prototype.getScanCount, () =>
-        it("should increment the scan count", () => expect(new ScanCounter(3123).getScanCount()).toBe(3123)),
-    );
-});