diff --git a/code/websites/pokedex.online/src/views/GamemasterExplorer.vue b/code/websites/pokedex.online/src/views/GamemasterExplorer.vue index ff251d9..e29d4f7 100644 --- a/code/websites/pokedex.online/src/views/GamemasterExplorer.vue +++ b/code/websites/pokedex.online/src/views/GamemasterExplorer.vue @@ -396,7 +396,43 @@ const preferences = useLocalStorage('gamemaster-explorer-prefs', { const clipboard = useClipboard(); const client = new GamemasterClient(); -// Computed +// Web Worker for search operations +let searchWorker = null; +let searchWorkerRequestId = 0; +const initSearchWorker = () => { + if (!searchWorker) { + searchWorker = new Worker( + new URL('../workers/search.worker.js', import.meta.url), + { type: 'module' } + ); + searchWorker.onmessage = handleSearchWorkerMessage; + } +}; + +const handleSearchWorkerMessage = (event) => { + const { type, id, results, percent, error: workerError } = event.data; + + if (type === 'progress') { + operationProgress.value.percent = percent; + operationProgress.value.message = `Searching... ${Math.round(percent)}%`; + } else if (type === 'complete') { + searchResults.value = results; + currentResultIndex.value = 0; + operationProgress.value.complete = true; + setTimeout(() => { + operationProgress.value.active = false; + }, 500); + // Scroll to first result if found + if (results.length > 0) { + nextTick(() => scrollToResult()); + } + } else if (type === 'error') { + console.error('Search worker error:', workerError); + operationProgress.value.active = false; + } +}; + +// Composables const hasFiles = computed(() => status.value.totalFiles > 0); const fileTooLarge = computed(() => fileLines.value.length > 10000); const lineHeight = computed(() => {