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">
|
<script lang="ts">
|
||||||
import { highlightMatch, highlightSnippet, stripHtmlButKeepHighlights } from '../highlightUtils';
|
import { highlightMatch, highlightSnippet, stripHtmlButKeepHighlights } from '../utils/highlight';
|
||||||
import type { DynamicContentItem } from '../dynamicSearch';
|
import type { DynamicContentItem } from '../utils/dynamicItems';
|
||||||
import type { FuseResultMatch } from '../core/types';
|
import type { FuseResultMatch } from '../core/types';
|
||||||
|
|
||||||
const { item, isSelected, searchTerm, matches } = $props<{
|
const { item, isSelected, searchTerm, matches } = $props<{
|
||||||
@@ -9,21 +9,6 @@
|
|||||||
searchTerm: string;
|
searchTerm: string;
|
||||||
matches?: readonly FuseResultMatch[];
|
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>
|
</script>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
+7
-7
@@ -3,14 +3,14 @@
|
|||||||
import { settingsState } from '@/seqta/utils/listeners/SettingsState'
|
import { settingsState } from '@/seqta/utils/listeners/SettingsState'
|
||||||
import { fade, scale } from 'svelte/transition';
|
import { fade, scale } from 'svelte/transition';
|
||||||
import { circOut, quintOut } from 'svelte/easing';
|
import { circOut, quintOut } from 'svelte/easing';
|
||||||
import { type StaticCommandItem } from './core/commands';
|
import { type StaticCommandItem } from '../core/commands';
|
||||||
import type { CombinedResult } from './core/types';
|
import type { CombinedResult } from '../core/types';
|
||||||
import { createSearchIndexes, performSearch as doSearch } from './searchUtils';
|
import { createSearchIndexes, performSearch as doSearch } from '../search/searchUtils';
|
||||||
import { highlightMatch, highlightSnippet, stripHtmlButKeepHighlights } from './highlightUtils';
|
import { highlightMatch, highlightSnippet, stripHtmlButKeepHighlights } from '../utils/highlight';
|
||||||
import Fuse from 'fuse.js';
|
import Fuse from 'fuse.js';
|
||||||
import Calculator from './components/Calculator.svelte';
|
import Calculator from './Calculator.svelte';
|
||||||
import { actionMap } from './indexing/actions';
|
import { actionMap } from '../indexing/actions';
|
||||||
import type { IndexItem, HydratedIndexItem } from './indexing/types';
|
import type { IndexItem, HydratedIndexItem } from '../indexing/types';
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
|
|
||||||
const {
|
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;
|
let currentAbortController: AbortController | null = null;
|
||||||
|
|
||||||
async function initWorker() {
|
async function initWorker() {
|
||||||
// Avoid re-initialization
|
|
||||||
if (isInitialized) {
|
if (isInitialized) {
|
||||||
console.debug("Vector worker already initialized.");
|
console.debug("Vector worker already initialized.");
|
||||||
return;
|
return;
|
||||||
+9
-11
@@ -1,14 +1,14 @@
|
|||||||
import Fuse, { type FuseResult } from "fuse.js";
|
import Fuse, { type FuseResult } from "fuse.js";
|
||||||
import { getStaticCommands, type StaticCommandItem } from "./core/commands";
|
import { getStaticCommands, type StaticCommandItem } from "../core/commands";
|
||||||
import { getDynamicItems } from "./dynamicSearch";
|
import { getDynamicItems } from "../utils/dynamicItems";
|
||||||
import type { CombinedResult } from "./core/types";
|
import type { CombinedResult } from "../core/types";
|
||||||
import type { HydratedIndexItem } from "./indexing/types";
|
import type { HydratedIndexItem } from "../indexing/types";
|
||||||
import { searchVectors } from "./search/vector/vectorSearch";
|
import { searchVectors } from "./vector/vectorSearch";
|
||||||
import type { VectorSearchResult } from "./search/vector/vectorTypes";
|
import type { VectorSearchResult } from "./vector/vectorTypes";
|
||||||
|
|
||||||
export function createSearchIndexes() {
|
export function createSearchIndexes() {
|
||||||
const commands = getStaticCommands();
|
const commands = getStaticCommands();
|
||||||
const dynamicItems = getDynamicItems(); // Returns HydratedIndexItem[]
|
const dynamicItems = getDynamicItems();
|
||||||
|
|
||||||
const commandOptions = {
|
const commandOptions = {
|
||||||
keys: ["text", "category", "keywords"],
|
keys: ["text", "category", "keywords"],
|
||||||
@@ -20,15 +20,13 @@ export function createSearchIndexes() {
|
|||||||
useExtendedSearch: false,
|
useExtendedSearch: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Keys for dynamic items remain the same structurally
|
|
||||||
const dynamicOptions = {
|
const dynamicOptions = {
|
||||||
keys: [
|
keys: [
|
||||||
"text",
|
"text",
|
||||||
"content",
|
"content",
|
||||||
"category",
|
"category",
|
||||||
"metadata.author", // Example: Include specific metadata if needed
|
"metadata.author",
|
||||||
"metadata.subject", // Example: Include specific metadata if needed
|
"metadata.subject",
|
||||||
// 'keywords', // Keywords are not currently part of IndexItem, add if needed
|
|
||||||
],
|
],
|
||||||
includeScore: true,
|
includeScore: true,
|
||||||
includeMatches: true,
|
includeMatches: true,
|
||||||
+1
-10
@@ -1,13 +1,4 @@
|
|||||||
/* import type { VectorSearchResult } from "./vectorTypes";
|
import { getEmbedding, EmbeddingIndex, initializeModel } from 'client-vector-search';
|
||||||
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 type { HydratedIndexItem } from '../../indexing/types';
|
import type { HydratedIndexItem } from '../../indexing/types';
|
||||||
import type { SearchResult } from 'client-vector-search';
|
import type { SearchResult } from 'client-vector-search';
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@ import notificationCollectorPlugin from './built-in/notificationCollector';
|
|||||||
import themesPlugin from './built-in/themes';
|
import themesPlugin from './built-in/themes';
|
||||||
import animatedBackgroundPlugin from './built-in/animatedBackground';
|
import animatedBackgroundPlugin from './built-in/animatedBackground';
|
||||||
import assessmentsAveragePlugin from './built-in/assessmentsAverage';
|
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';
|
import testPlugin from './built-in/test';
|
||||||
|
|
||||||
// Initialize plugin manager
|
// Initialize plugin manager
|
||||||
|
|||||||
Reference in New Issue
Block a user