mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-05 19:24:39 +00:00
feat: cleaned code + improved performance
This commit is contained in:
@@ -1,141 +0,0 @@
|
||||
import type { Plugin } from "@/plugins/core/types";
|
||||
import { BasePlugin } from "@/plugins/core/settings";
|
||||
import {
|
||||
booleanSetting,
|
||||
defineSettings,
|
||||
Setting,
|
||||
stringSetting,
|
||||
} from "@/plugins/core/settingsHelpers";
|
||||
import renderSvelte from "@/interface/main";
|
||||
import SearchBar from "../SearchBar.svelte";
|
||||
import styles from "./styles.css?inline";
|
||||
import { unmount } from "svelte";
|
||||
import { waitForElm } from "@/seqta/utils/waitForElm";
|
||||
import { runIndexing } from "../indexing/indexer";
|
||||
import { VectorWorkerManager } from "../indexing/worker/vectorWorkerManager";
|
||||
import { initVectorSearch } from "../search/vector/vectorSearch";
|
||||
|
||||
const settings = defineSettings({
|
||||
searchHotkey: stringSetting({
|
||||
default: "ctrl+k",
|
||||
title: "Search Hotkey",
|
||||
description: "Keyboard shortcut to open the search (cmd on Mac)",
|
||||
}),
|
||||
showRecentFirst: booleanSetting({
|
||||
default: true,
|
||||
title: "Show Recent First",
|
||||
description: "Sort dynamic content by most recent first",
|
||||
}),
|
||||
transparencyEffects: booleanSetting({
|
||||
default: true,
|
||||
title: "Transparency Effects",
|
||||
description: "Enable transparency effects for the search bar",
|
||||
}),
|
||||
runIndexingOnLoad: booleanSetting({
|
||||
default: true,
|
||||
title: "Index on Page Load",
|
||||
description: "Run content indexing when SEQTA loads",
|
||||
}),
|
||||
});
|
||||
|
||||
class GlobalSearchPlugin extends BasePlugin<typeof settings> {
|
||||
@Setting(settings.searchHotkey)
|
||||
searchHotkey!: string;
|
||||
|
||||
@Setting(settings.showRecentFirst)
|
||||
showRecentFirst!: boolean;
|
||||
|
||||
@Setting(settings.transparencyEffects)
|
||||
transparencyEffects!: boolean;
|
||||
|
||||
@Setting(settings.runIndexingOnLoad)
|
||||
runIndexingOnLoad!: boolean;
|
||||
}
|
||||
|
||||
const settingsInstance = new GlobalSearchPlugin();
|
||||
|
||||
const globalSearchPlugin: Plugin<typeof settings> = {
|
||||
id: "global-search",
|
||||
name: "Global Search",
|
||||
description: "Quick search for everything in SEQTA",
|
||||
version: "1.0.0",
|
||||
settings: settingsInstance.settings,
|
||||
disableToggle: true,
|
||||
styles: styles,
|
||||
|
||||
run: async (api) => {
|
||||
let app: any;
|
||||
|
||||
initVectorSearch();
|
||||
|
||||
// Run initial indexing and update dynamic items
|
||||
if (api.settings.runIndexingOnLoad) {
|
||||
setTimeout(async () => {
|
||||
await runIndexing();
|
||||
}, 2000); // Delay initial indexing to let page load
|
||||
}
|
||||
|
||||
const mountSearchBar = (titleElement: Element) => {
|
||||
if (titleElement.querySelector(".search-trigger")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const searchButton = document.createElement("div");
|
||||
searchButton.className = "search-trigger";
|
||||
searchButton.innerHTML = /* html */ `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
|
||||
<p>Quick search...</p>
|
||||
<span style="margin-left: auto; display: flex; align-items: center; color: #777; font-size: 12px;">⌘K</span>
|
||||
`;
|
||||
|
||||
titleElement.appendChild(searchButton);
|
||||
|
||||
const searchRoot = document.createElement("div");
|
||||
document.body.appendChild(searchRoot);
|
||||
const searchRootShadow = searchRoot.attachShadow({ mode: "open" });
|
||||
|
||||
console.log("adding event listener to search button");
|
||||
|
||||
searchButton.addEventListener("click", () => {
|
||||
console.log("search button clicked");
|
||||
// @ts-ignore - Intentionally adding to window
|
||||
window.setCommandPalleteOpen(true);
|
||||
});
|
||||
|
||||
try {
|
||||
app = renderSvelte(SearchBar, searchRootShadow, {
|
||||
transparencyEffects: api.settings.transparencyEffects ? true : false,
|
||||
showRecentFirst: api.settings.showRecentFirst,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error rendering Svelte component:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const title = document.querySelector("#title");
|
||||
|
||||
if (title) {
|
||||
mountSearchBar(title);
|
||||
} else {
|
||||
await waitForElm("#title", true, 100, 60);
|
||||
mountSearchBar(document.querySelector("#title") as Element);
|
||||
}
|
||||
|
||||
return () => {
|
||||
const searchButton = document.querySelector(".search-trigger");
|
||||
const searchRoot = document.querySelector(".global-search-root");
|
||||
if (searchButton) searchButton.remove();
|
||||
if (searchRoot) searchRoot.remove();
|
||||
|
||||
// Clean up workers
|
||||
VectorWorkerManager.getInstance().terminate();
|
||||
unmount(app);
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export default globalSearchPlugin;
|
||||
+2
-17
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { highlightMatch, highlightSnippet, stripHtmlButKeepHighlights } from '../highlightUtils';
|
||||
import type { DynamicContentItem } from '../dynamicSearch';
|
||||
import { highlightMatch, highlightSnippet, stripHtmlButKeepHighlights } from '../utils/highlight';
|
||||
import type { DynamicContentItem } from '../utils/dynamicItems';
|
||||
import type { FuseResultMatch } from '../core/types';
|
||||
|
||||
const { item, isSelected, searchTerm, matches } = $props<{
|
||||
@@ -9,21 +9,6 @@
|
||||
searchTerm: string;
|
||||
matches?: readonly FuseResultMatch[];
|
||||
}>();
|
||||
|
||||
/* const dueDate = $derived(item.metadata?.dueDate
|
||||
? new Date(item.metadata.dueDate)
|
||||
: null); */
|
||||
|
||||
/* const formattedDueDate = $derived(dueDate
|
||||
? dueDate.toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})
|
||||
: 'No due date'); */
|
||||
|
||||
//const isPastDue = $derived(dueDate ? dueDate.getTime() < Date.now() : false);
|
||||
</script>
|
||||
|
||||
<button
|
||||
+7
-7
@@ -3,14 +3,14 @@
|
||||
import { settingsState } from '@/seqta/utils/listeners/SettingsState'
|
||||
import { fade, scale } from 'svelte/transition';
|
||||
import { circOut, quintOut } from 'svelte/easing';
|
||||
import { type StaticCommandItem } from './core/commands';
|
||||
import type { CombinedResult } from './core/types';
|
||||
import { createSearchIndexes, performSearch as doSearch } from './searchUtils';
|
||||
import { highlightMatch, highlightSnippet, stripHtmlButKeepHighlights } from './highlightUtils';
|
||||
import { type StaticCommandItem } from '../core/commands';
|
||||
import type { CombinedResult } from '../core/types';
|
||||
import { createSearchIndexes, performSearch as doSearch } from '../search/searchUtils';
|
||||
import { highlightMatch, highlightSnippet, stripHtmlButKeepHighlights } from '../utils/highlight';
|
||||
import Fuse from 'fuse.js';
|
||||
import Calculator from './components/Calculator.svelte';
|
||||
import { actionMap } from './indexing/actions';
|
||||
import type { IndexItem, HydratedIndexItem } from './indexing/types';
|
||||
import Calculator from './Calculator.svelte';
|
||||
import { actionMap } from '../indexing/actions';
|
||||
import type { IndexItem, HydratedIndexItem } from '../indexing/types';
|
||||
import debounce from 'lodash/debounce';
|
||||
|
||||
const {
|
||||
@@ -0,0 +1,89 @@
|
||||
import type { Plugin } from "@/plugins/core/types";
|
||||
import { BasePlugin } from "@/plugins/core/settings";
|
||||
import {
|
||||
booleanSetting,
|
||||
defineSettings,
|
||||
Setting,
|
||||
stringSetting,
|
||||
} from "@/plugins/core/settingsHelpers";
|
||||
import styles from "./styles.css?inline";
|
||||
import { waitForElm } from "@/seqta/utils/waitForElm";
|
||||
import { runIndexing } from "../indexing/indexer";
|
||||
import { initVectorSearch } from "../search/vector/vectorSearch";
|
||||
import { mountSearchBar, cleanupSearchBar } from "./mountSearchBar";
|
||||
|
||||
const settings = defineSettings({
|
||||
searchHotkey: stringSetting({
|
||||
default: "ctrl+k",
|
||||
title: "Search Hotkey",
|
||||
description: "Keyboard shortcut to open the search (cmd on Mac)",
|
||||
}),
|
||||
showRecentFirst: booleanSetting({
|
||||
default: true,
|
||||
title: "Show Recent First",
|
||||
description: "Sort dynamic content by most recent first",
|
||||
}),
|
||||
transparencyEffects: booleanSetting({
|
||||
default: true,
|
||||
title: "Transparency Effects",
|
||||
description: "Enable transparency effects for the search bar",
|
||||
}),
|
||||
runIndexingOnLoad: booleanSetting({
|
||||
default: true,
|
||||
title: "Index on Page Load",
|
||||
description: "Run content indexing when SEQTA loads",
|
||||
}),
|
||||
});
|
||||
|
||||
class GlobalSearchPlugin extends BasePlugin<typeof settings> {
|
||||
@Setting(settings.searchHotkey)
|
||||
searchHotkey!: string;
|
||||
|
||||
@Setting(settings.showRecentFirst)
|
||||
showRecentFirst!: boolean;
|
||||
|
||||
@Setting(settings.transparencyEffects)
|
||||
transparencyEffects!: boolean;
|
||||
|
||||
@Setting(settings.runIndexingOnLoad)
|
||||
runIndexingOnLoad!: boolean;
|
||||
}
|
||||
|
||||
const settingsInstance = new GlobalSearchPlugin();
|
||||
|
||||
const globalSearchPlugin: Plugin<typeof settings> = {
|
||||
id: "global-search",
|
||||
name: "Global Search",
|
||||
description: "Quick search for everything in SEQTA",
|
||||
version: "1.0.0",
|
||||
settings: settingsInstance.settings,
|
||||
disableToggle: true,
|
||||
styles: styles,
|
||||
|
||||
run: async (api) => {
|
||||
const appRef = { current: null };
|
||||
|
||||
initVectorSearch();
|
||||
|
||||
if (api.settings.runIndexingOnLoad) {
|
||||
setTimeout(async () => {
|
||||
await runIndexing();
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
const title = document.querySelector("#title");
|
||||
|
||||
if (title) {
|
||||
mountSearchBar(title, api, appRef);
|
||||
} else {
|
||||
await waitForElm("#title", true, 100, 60);
|
||||
mountSearchBar(document.querySelector("#title") as Element, api, appRef);
|
||||
}
|
||||
|
||||
return () => {
|
||||
cleanupSearchBar(appRef);
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export default globalSearchPlugin;
|
||||
@@ -0,0 +1,56 @@
|
||||
import renderSvelte from "@/interface/main";
|
||||
import SearchBar from "../components/SearchBar.svelte";
|
||||
import { unmount } from "svelte";
|
||||
import { VectorWorkerManager } from "../indexing/worker/vectorWorkerManager";
|
||||
|
||||
export function mountSearchBar(
|
||||
titleElement: Element,
|
||||
api: any,
|
||||
appRef: { current: any }
|
||||
) {
|
||||
if (titleElement.querySelector(".search-trigger")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const searchButton = document.createElement("div");
|
||||
searchButton.className = "search-trigger";
|
||||
searchButton.innerHTML = /* html */ `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
<p>Quick search...</p>
|
||||
<span style="margin-left: auto; display: flex; align-items: center; color: #777; font-size: 12px;">⌘K</span>
|
||||
`;
|
||||
|
||||
titleElement.appendChild(searchButton);
|
||||
|
||||
const searchRoot = document.createElement("div");
|
||||
document.body.appendChild(searchRoot);
|
||||
const searchRootShadow = searchRoot.attachShadow({ mode: "open" });
|
||||
|
||||
searchButton.addEventListener("click", () => {
|
||||
// @ts-ignore - Intentionally adding to window
|
||||
window.setCommandPalleteOpen(true);
|
||||
});
|
||||
|
||||
try {
|
||||
appRef.current = renderSvelte(SearchBar, searchRootShadow, {
|
||||
transparencyEffects: api.settings.transparencyEffects ? true : false,
|
||||
showRecentFirst: api.settings.showRecentFirst,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error rendering Svelte component:", error);
|
||||
}
|
||||
}
|
||||
|
||||
export function cleanupSearchBar(appRef: { current: any }) {
|
||||
const searchButton = document.querySelector(".search-trigger");
|
||||
const searchRoot = document.querySelector(".global-search-root");
|
||||
if (searchButton) searchButton.remove();
|
||||
if (searchRoot) searchRoot.remove();
|
||||
|
||||
// Clean up workers
|
||||
VectorWorkerManager.getInstance().terminate();
|
||||
unmount(appRef.current);
|
||||
}
|
||||
-1
@@ -10,7 +10,6 @@ let isInitialized = false;
|
||||
let currentAbortController: AbortController | null = null;
|
||||
|
||||
async function initWorker() {
|
||||
// Avoid re-initialization
|
||||
if (isInitialized) {
|
||||
console.debug("Vector worker already initialized.");
|
||||
return;
|
||||
+9
-11
@@ -1,14 +1,14 @@
|
||||
import Fuse, { type FuseResult } from "fuse.js";
|
||||
import { getStaticCommands, type StaticCommandItem } from "./core/commands";
|
||||
import { getDynamicItems } from "./dynamicSearch";
|
||||
import type { CombinedResult } from "./core/types";
|
||||
import type { HydratedIndexItem } from "./indexing/types";
|
||||
import { searchVectors } from "./search/vector/vectorSearch";
|
||||
import type { VectorSearchResult } from "./search/vector/vectorTypes";
|
||||
import { getStaticCommands, type StaticCommandItem } from "../core/commands";
|
||||
import { getDynamicItems } from "../utils/dynamicItems";
|
||||
import type { CombinedResult } from "../core/types";
|
||||
import type { HydratedIndexItem } from "../indexing/types";
|
||||
import { searchVectors } from "./vector/vectorSearch";
|
||||
import type { VectorSearchResult } from "./vector/vectorTypes";
|
||||
|
||||
export function createSearchIndexes() {
|
||||
const commands = getStaticCommands();
|
||||
const dynamicItems = getDynamicItems(); // Returns HydratedIndexItem[]
|
||||
const dynamicItems = getDynamicItems();
|
||||
|
||||
const commandOptions = {
|
||||
keys: ["text", "category", "keywords"],
|
||||
@@ -20,15 +20,13 @@ export function createSearchIndexes() {
|
||||
useExtendedSearch: false,
|
||||
};
|
||||
|
||||
// Keys for dynamic items remain the same structurally
|
||||
const dynamicOptions = {
|
||||
keys: [
|
||||
"text",
|
||||
"content",
|
||||
"category",
|
||||
"metadata.author", // Example: Include specific metadata if needed
|
||||
"metadata.subject", // Example: Include specific metadata if needed
|
||||
// 'keywords', // Keywords are not currently part of IndexItem, add if needed
|
||||
"metadata.author",
|
||||
"metadata.subject",
|
||||
],
|
||||
includeScore: true,
|
||||
includeMatches: true,
|
||||
+1
-10
@@ -1,13 +1,4 @@
|
||||
/* import type { VectorSearchResult } from "./vectorTypes";
|
||||
import { VectorWorkerManager } from '../../indexing/worker/vectorWorkerManager';
|
||||
|
||||
export function searchVectors(query: string, topK: number = 10): Promise<VectorSearchResult[]> {
|
||||
// Use the single instance of the VectorWorkerManager (from indexing) to perform the search
|
||||
return VectorWorkerManager.getInstance().search(query, topK);
|
||||
}
|
||||
*/
|
||||
|
||||
import { EmbeddingIndex, getEmbedding, initializeModel } from 'client-vector-search';
|
||||
import { getEmbedding, EmbeddingIndex, initializeModel } from 'client-vector-search';
|
||||
import type { HydratedIndexItem } from '../../indexing/types';
|
||||
import type { SearchResult } from 'client-vector-search';
|
||||
|
||||
@@ -6,7 +6,7 @@ import notificationCollectorPlugin from './built-in/notificationCollector';
|
||||
import themesPlugin from './built-in/themes';
|
||||
import animatedBackgroundPlugin from './built-in/animatedBackground';
|
||||
import assessmentsAveragePlugin from './built-in/assessmentsAverage';
|
||||
import globalSearchPlugin from './built-in/globalSearch/core';
|
||||
import globalSearchPlugin from './built-in/globalSearch/src/core';
|
||||
import testPlugin from './built-in/test';
|
||||
|
||||
// Initialize plugin manager
|
||||
|
||||
Reference in New Issue
Block a user