feat: cleaned code + improved performance

This commit is contained in:
SethBurkart123
2025-05-01 21:31:07 +10:00
parent d9b1482255
commit d4127626b1
26 changed files with 165 additions and 188 deletions
@@ -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;
@@ -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
@@ -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);
}
@@ -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;
@@ -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,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';