From 1f3354c47bc1fab73c40f6e529db512aec6cc033 Mon Sep 17 00:00:00 2001 From: SethBurkart123 Date: Mon, 31 Mar 2025 22:54:03 +1100 Subject: [PATCH] feat: add global search UI --- package.json | 1 + .../built-in/globalSearch/SearchBar.svelte | 202 ++++++++++++++++++ src/plugins/built-in/globalSearch/index.ts | 93 ++++++++ src/plugins/built-in/globalSearch/styles.css | 68 ++++++ src/plugins/index.ts | 3 + 5 files changed, 367 insertions(+) create mode 100644 src/plugins/built-in/globalSearch/SearchBar.svelte create mode 100644 src/plugins/built-in/globalSearch/index.ts create mode 100644 src/plugins/built-in/globalSearch/styles.css diff --git a/package.json b/package.json index 3cd068bf..1ccada5d 100644 --- a/package.json +++ b/package.json @@ -80,6 +80,7 @@ "dompurify": "^3.2.4", "embla-carousel-autoplay": "^8.5.2", "embla-carousel-svelte": "^8.5.2", + "flexsearch": "^0.8.147", "fuse.js": "^7.1.0", "idb": "^8.0.2", "localforage": "^1.10.0", diff --git a/src/plugins/built-in/globalSearch/SearchBar.svelte b/src/plugins/built-in/globalSearch/SearchBar.svelte new file mode 100644 index 00000000..82de5623 --- /dev/null +++ b/src/plugins/built-in/globalSearch/SearchBar.svelte @@ -0,0 +1,202 @@ + + +{#if commandPalleteOpen} + +{/if} \ No newline at end of file diff --git a/src/plugins/built-in/globalSearch/index.ts b/src/plugins/built-in/globalSearch/index.ts new file mode 100644 index 00000000..2d5116d5 --- /dev/null +++ b/src/plugins/built-in/globalSearch/index.ts @@ -0,0 +1,93 @@ +import type { Plugin } from '@/plugins/core/types'; +import { BasePlugin } from '@/plugins/core/settings'; +import { booleanSetting, defineSettings, Setting, stringSetting } from '@/plugins/core/settingsHelpers'; +//import FlexSearch from 'flexsearch'; +import renderSvelte from '@/interface/main'; +import SearchBar from './SearchBar.svelte'; +import styles from './styles.css?inline'; +import { unmount } from 'svelte'; + +// Plugin settings +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', + }) +}); + +class GlobalSearchPlugin extends BasePlugin { + @Setting(settings.searchHotkey) + searchHotkey!: string; + + @Setting(settings.showRecentFirst) + showRecentFirst!: boolean; +} + +const settingsInstance = new GlobalSearchPlugin(); + +const globalSearchPlugin: Plugin = { + id: 'global-search', + name: 'Global Search', + description: 'Quick search for everything in SEQTA', + version: '1.0.0', + settings: settingsInstance.settings, + disableToggle: true, + + // Add some basic styles for our search UI + styles: styles, + + run: async (api) => { + let app: any; + + // Create search button + api.seqta.onMount('#title', (titleElement) => { + // Create search button + const searchButton = document.createElement('div'); + searchButton.className = 'search-trigger'; + searchButton.innerHTML = ` + + + + + +

Quick search...

+ ⌘K + `; + + // Add button before the title + titleElement.appendChild(searchButton); + + // Create shadow DOM for Svelte component + const searchRoot = document.createElement('div'); + document.body.appendChild(searchRoot); + const searchRootShadow = searchRoot.attachShadow({ mode: 'open' }); + + // Mount Svelte component in shadow DOM + app = renderSvelte(SearchBar, searchRootShadow); + + // Handle click on search button + searchButton.addEventListener('click', () => { + // @ts-ignore + window.setCommandPalleteOpen(true); + }); + }); + + // Clean up + return () => { + const searchButton = document.querySelector('.search-trigger'); + const searchRoot = document.querySelector('.global-search-root'); + if (searchButton) searchButton.remove(); + if (searchRoot) searchRoot.remove(); + + unmount(app); + }; + } +}; + +export default globalSearchPlugin; \ No newline at end of file diff --git a/src/plugins/built-in/globalSearch/styles.css b/src/plugins/built-in/globalSearch/styles.css new file mode 100644 index 00000000..3a9911be --- /dev/null +++ b/src/plugins/built-in/globalSearch/styles.css @@ -0,0 +1,68 @@ +.search-trigger { + display: flex; + align-items: center; + justify-content: center; + height: 32px; + margin-left: 10px; + border-radius: 8px; + cursor: pointer; + transition: all 0.2s ease; + margin-right: auto !important; + padding: 3px 12px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); + backdrop-filter: blur(4px); + + svg { + opacity: 0.8; + } + + p { + font-size: 14px; + margin-left: 8px; + margin-right: 16px; + height: 100%; + margin-bottom: 0; + line-height: 32px; + font-weight: 400; + } +} + +/* Light mode styles */ +.search-trigger { + background-color: rgba(248, 250, 252, 0.9) !important; + border: 1px solid rgba(0, 0, 0, 0.1) !important; + color: #555 !important; + + p { + color: #555 !important; + } + + svg { + color: #555; + } +} + +.search-trigger:hover { + background-color: rgba(248, 250, 252, 1) !important; + color: #333 !important; +} + +/* Dark mode styles */ +.dark .search-trigger { + background-color: rgba(30, 41, 59, 0.7) !important; + border: 1px solid rgba(255, 255, 255, 0.1) !important; + color: #aaa !important; + + p { + color: #aaa !important; + } + + svg { + color: #aaa; + } +} + +.dark .search-trigger:hover { + background-color: rgba(30, 41, 59, 0.9) !important; + color: #eee !important; +} \ No newline at end of file diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 5332e966..539600d5 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -6,6 +6,8 @@ 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'; + // Initialize plugin manager const pluginManager = PluginManager.getInstance(); @@ -15,6 +17,7 @@ pluginManager.registerPlugin(animatedBackgroundPlugin); pluginManager.registerPlugin(assessmentsAveragePlugin); pluginManager.registerPlugin(notificationCollectorPlugin); pluginManager.registerPlugin(timetablePlugin); +pluginManager.registerPlugin(globalSearchPlugin); //pluginManager.registerPlugin(testPlugin); export { init as Monofile } from './monofile';