diff --git a/assets/styles.css b/assets/styles.css index eccf2d5..2e8501e 100644 --- a/assets/styles.css +++ b/assets/styles.css @@ -18,6 +18,7 @@ } .omnisearch-result__title { + white-space: pre-wrap; align-items: center; display: flex; gap: 5px; diff --git a/manifest-beta.json b/manifest-beta.json index 7bc66fd..7ea5551 100644 --- a/manifest-beta.json +++ b/manifest-beta.json @@ -1,7 +1,7 @@ { "id": "omnisearch", "name": "Omnisearch", - "version": "1.14.1-beta.3", + "version": "1.14.1-beta.2", "minAppVersion": "1.0.0", "description": "A search engine that just works", "author": "Simon Cambier", diff --git a/package.json b/package.json index b59c13f..026c0e8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "scambier.obsidian-search", - "version": "1.14.0", + "version": "1.14.1-beta.3", "description": "A search engine for Obsidian", "main": "dist/main.js", "scripts": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4558293..640b074 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -58,7 +58,7 @@ devDependencies: esbuild-plugin-copy: 1.3.0_esbuild@0.14.0 esbuild-svelte: 0.7.1_wvi5wuag3veo5vm52k3h7pgaae jest: 27.5.1 - obsidian: 1.1.1 + obsidian: 1.2.8 prettier: 2.8.1 prettier-plugin-svelte: 2.8.1_sro2v6ld777payjtkjtiuogcxi svelte: 3.54.0 @@ -4119,8 +4119,8 @@ packages: object-keys: 1.1.1 dev: true - /obsidian/1.1.1: - resolution: {integrity: sha512-GcxhsHNkPEkwHEjeyitfYNBcQuYGeAHFs1pEpZIv0CnzSfui8p8bPLm2YKLgcg20B764770B1sYGtxCvk9ptxg==} + /obsidian/1.2.8: + resolution: {integrity: sha512-HrC+feA8o0tXspj4lEAqxb1btwLwHD2oHXSwbbN+CdRHURqbCkuIDLld+nkuyJ1w1c9uvVDRVk8BoeOnWheOrQ==} peerDependencies: '@codemirror/state': ^6.0.0 '@codemirror/view': ^6.0.0 diff --git a/src/components/InputSearch.svelte b/src/components/InputSearch.svelte index e649c54..15b4df2 100644 --- a/src/components/InputSearch.svelte +++ b/src/components/InputSearch.svelte @@ -5,24 +5,26 @@ import { cacheManager } from '../cache-manager' export let initialValue = '' - let initialSet = false export let placeholder = '' + let initialSet = false let value = '' let elInput: HTMLInputElement const dispatch = createEventDispatcher() - export function setInputValue(v:string): void { + export function setInputValue(v: string): void { value = v } - $: { - if (initialValue && !initialSet && !value) { + function watchInitialValue(v: string): void { + if (v && !initialSet && !value) { initialSet = true - value = initialValue + value = v selectInput() } } + $: watchInitialValue(initialValue) + function selectInput(_?: HTMLElement): void { tick() .then(() => { @@ -39,14 +41,14 @@ // the next time we open the modal, the search field will be empty cacheManager.addToSearchHistory('') dispatch('input', value) - }, 250) + }, 300)
{ @@ -112,9 +128,20 @@ refInput?.setInputValue(searchQuery) } + let cancelableQuery: CancelablePromise | null = null async function updateResults() { + // If search is already in progress, cancel it and start a new one + if (cancelableQuery) { + cancelableQuery.cancel() + cancelableQuery = null + } query = new Query(searchQuery) - resultNotes = await searchEngine.getSuggestions(query) + cancelableQuery = cancelable( + new Promise(resolve => { + resolve(searchEngine.getSuggestions(query)) + }) + ) + resultNotes = await cancelableQuery selectedIndex = 0 await scrollIntoView() } @@ -297,7 +324,7 @@ to cycle history
- to open + {openInCurrentPaneKey}to open
tab @@ -305,15 +332,15 @@
- {getCtrlKeyLabel()} ↵ + {openInNewPaneKey} to open in a new pane
- shift ↵ + {createInCurrentPaneKey} to create
- ctrl shift ↵ + {createInNewPaneKey} to create in a new pane
diff --git a/src/components/ResultItemVault.svelte b/src/components/ResultItemVault.svelte index 7cf310e..3360581 100644 --- a/src/components/ResultItemVault.svelte +++ b/src/components/ResultItemVault.svelte @@ -71,8 +71,9 @@ {@html title.replace(reg, highlighterGroups)} - .{getExtension(note.path)} + + .{getExtension(note.path)} + {#if note.matches.length > 0} diff --git a/src/components/modals.ts b/src/components/modals.ts index 7b16729..aa86114 100644 --- a/src/components/modals.ts +++ b/src/components/modals.ts @@ -1,7 +1,9 @@ import { App, Modal, TFile } from 'obsidian' +import type { Modifier } from 'obsidian' import ModalVault from './ModalVault.svelte' import ModalInFile from './ModalInFile.svelte' import { eventBus, EventNames, isInputComposition } from '../globals' +import { settings } from '../settings' abstract class OmnisearchModal extends Modal { protected constructor(app: App) { @@ -61,8 +63,24 @@ abstract class OmnisearchModal extends Modal { // #endregion Up/Down navigation + let openInCurrentPaneKey: Modifier[] + let openInNewPaneKey: Modifier[] + let createInCurrentPaneKey: Modifier[] + let createInNewPaneKey: Modifier[] + if (settings.openInNewPane) { + openInCurrentPaneKey = ['Mod'] + openInNewPaneKey = [] + createInCurrentPaneKey = ['Mod', 'Shift'] + createInNewPaneKey = ['Shift'] + } else { + openInCurrentPaneKey = [] + openInNewPaneKey = ['Mod'] + createInCurrentPaneKey = ['Shift'] + createInNewPaneKey = ['Mod', 'Shift'] + } + // Open in new pane - this.scope.register(['Mod'], 'Enter', e => { + this.scope.register(openInNewPaneKey, 'Enter', e => { e.preventDefault() eventBus.emit('open-in-new-pane') }) @@ -74,17 +92,17 @@ abstract class OmnisearchModal extends Modal { }) // Create a new note - this.scope.register(['Shift'], 'Enter', e => { + this.scope.register(createInCurrentPaneKey, 'Enter', e => { e.preventDefault() eventBus.emit('create-note') }) - this.scope.register(['Ctrl', 'Shift'], 'Enter', e => { + this.scope.register(createInNewPaneKey, 'Enter', e => { e.preventDefault() eventBus.emit('create-note', { newLeaf: true }) }) // Open in current pane - this.scope.register([], 'Enter', e => { + this.scope.register(openInCurrentPaneKey, 'Enter', e => { if (!isInputComposition()) { // Check if the user is still typing e.preventDefault() diff --git a/src/globals.ts b/src/globals.ts index 49ce0be..db14f9b 100644 --- a/src/globals.ts +++ b/src/globals.ts @@ -16,6 +16,7 @@ export const excerptAfter = 300 export const highlightClass = `suggestion-highlight omnisearch-highlight ${ settings.highlight ? 'omnisearch-default-highlight' : '' }` +export const K_DISABLE_OMNISEARCH = 'omnisearch-disabled' export const eventBus = new EventBus() diff --git a/src/main.ts b/src/main.ts index 0f1e655..4e9566d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,6 +4,7 @@ import { OmnisearchVaultModal, } from './components/modals' import { + isPluginDisabled, loadSettings, saveSettings, settings, @@ -29,6 +30,13 @@ export default class OmnisearchPlugin extends Plugin { async onload(): Promise { await loadSettings(this) + this.addSettingTab(new SettingsTab(this)) + + if (isPluginDisabled()) { + console.log('Omnisearch - Plugin disabled') + return + } + await cleanOldCacheFiles() await OmnisearchCache.clearOldDatabases() @@ -38,7 +46,6 @@ export default class OmnisearchPlugin extends Plugin { this.addRibbonButton() } - this.addSettingTab(new SettingsTab(this)) eventBus.disable('vault') eventBus.disable('infile') eventBus.on('global', EventNames.ToggleExcerpts, () => { @@ -252,3 +259,4 @@ function registerAPI(plugin: OmnisearchPlugin): void { // Deprecated ;(app as any).plugins.plugins.omnisearch.api = api } + diff --git a/src/search/omnisearch.ts b/src/search/omnisearch.ts index 9db3177..3094682 100644 --- a/src/search/omnisearch.ts +++ b/src/search/omnisearch.ts @@ -201,13 +201,16 @@ export class Omnisearch { logDebug('Found', results.length, 'results') - // Filter query results to only keep files that match query.extensions (if any) - if (query.extensions.length) { + // Filter query results to only keep files that match query.query.ext (if any) + if (query.query.ext?.length) { results = results.filter(r => { // ".can" should match ".canvas" const ext = '.' + r.id.split('.').pop() - return query.extensions.some(e => ext.startsWith(e)) + return query.query.ext?.some(e => + ext.startsWith(e.startsWith('.') ? e : '.' + e) + ) }) + console.log(query.query.ext, results.length) } // Filter query results that match the path @@ -219,15 +222,14 @@ export class Omnisearch { ) } if (query.query.exclude.path) { - results = results.filter(r => - !query.query.exclude.path?.some(p => - (r.id as string).toLowerCase().includes(p.toLowerCase()) - ) + results = results.filter( + r => + !query.query.exclude.path?.some(p => + (r.id as string).toLowerCase().includes(p.toLowerCase()) + ) ) } - // If the query does not return any result, - // retry but with a shorter prefix limit if (!results.length) { return [] } diff --git a/src/settings.ts b/src/settings.ts index 9f8b536..32d2485 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -7,7 +7,7 @@ import { } from 'obsidian' import { writable } from 'svelte/store' import { database } from './database' -import { getTextExtractor, isCacheEnabled } from './globals' +import { K_DISABLE_OMNISEARCH, getTextExtractor, isCacheEnabled } from './globals' import type OmnisearchPlugin from './main' interface WeightingSettings { @@ -47,6 +47,7 @@ export interface OmnisearchSettings extends WeightingSettings { simpleSearch: boolean highlight: boolean splitCamelCase: boolean + openInNewPane: boolean verboseLogging: boolean } @@ -55,6 +56,8 @@ export interface OmnisearchSettings extends WeightingSettings { */ export const showExcerpt = writable(false) +const needsARestart = `Needs a restart to fully take effect.` + export class SettingsTab extends PluginSettingTab { plugin: OmnisearchPlugin @@ -72,6 +75,11 @@ export class SettingsTab extends PluginSettingTab { const { containerEl } = this containerEl.empty() + if (app.loadLocalStorage(K_DISABLE_OMNISEARCH) == '1') { + const span = containerEl.createEl('span') + span.innerHTML = `⚠️ OMNISEARCH IS DISABLED ⚠️` + } + // Settings main title containerEl.createEl('h2', { text: 'Omnisearch' }) @@ -138,7 +146,7 @@ export class SettingsTab extends PluginSettingTab { Add extensions separated by a space, without the dot. Example: "txt org".
⚠️ Using extensions of non-plaintext files (like .docx or .pptx) WILL cause crashes, because Omnisearch will try to index their content.
- Needs a restart to fully take effect.` + ${needsARestart}` }) new Setting(containerEl) .setName('Additional files to index') @@ -192,7 +200,7 @@ export class SettingsTab extends PluginSettingTab { span.innerHTML = `Normalize diacritics in search terms. Words like "brûlée" or "žluťoučký" will be indexed as "brulee" and "zlutoucky".
⚠️ You probably should NOT disable this.
⚠️ Changing this setting will clear the cache.
- Needs a restart to fully take effect. + ${needsARestart} ` }) new Setting(containerEl) @@ -211,7 +219,7 @@ export class SettingsTab extends PluginSettingTab { camelCaseDesc.createSpan({}, span => { span.innerHTML = `Enable this if you want to be able to search for CamelCaseWords as separate words.
⚠️ Changing this setting will clear the cache.
- Needs a restart to fully take effect. + ${needsARestart} ` }) new Setting(containerEl) @@ -239,6 +247,18 @@ export class SettingsTab extends PluginSettingTab { }) ) + new Setting(containerEl) + .setName('Open in new pane') + .setDesc( + 'Open and create files in a new pane instead of the current pane.' + ) + .addToggle(toggle => + toggle.setValue(settings.openInNewPane).onChange(async v => { + settings.openInNewPane = v + await saveSettings(this.plugin) + }) + ) + //#endregion Behavior //#region User Interface @@ -364,7 +384,9 @@ export class SettingsTab extends PluginSettingTab { new Setting(containerEl) .setName('Enable verbose logging') - .setDesc('Adds a LOT of logs for debugging purposes. Don\'t forget to disable it.') + .setDesc( + "Adds a LOT of logs for debugging purposes. Don't forget to disable it." + ) .addToggle(toggle => toggle.setValue(settings.verboseLogging).onChange(async v => { settings.verboseLogging = v @@ -375,14 +397,33 @@ export class SettingsTab extends PluginSettingTab { //#endregion Debugginh //#region Danger Zone - if (isCacheEnabled()) { - new Setting(containerEl).setName('Danger Zone').setHeading() + new Setting(containerEl).setName('Danger Zone').setHeading() + const disableDesc = new DocumentFragment() + disableDesc.createSpan({}, span => { + span.innerHTML = `Disable Omnisearch on this device only.
+ ${needsARestart}` + }) + new Setting(containerEl) + .setName('Disable on this device') + .setDesc(disableDesc) + .addToggle(toggle => + toggle.setValue(isPluginDisabled()).onChange(async v => { + if (v) { + app.saveLocalStorage(K_DISABLE_OMNISEARCH, '1') + } else { + app.saveLocalStorage(K_DISABLE_OMNISEARCH) // No value = unset + } + new Notice('Omnisearch - Disabled. Please restart Obsidian.') + }) + ) + + if (isCacheEnabled()) { const resetCacheDesc = new DocumentFragment() resetCacheDesc.createSpan({}, span => { span.innerHTML = `Erase all Omnisearch cache data. - Use this if Omnisearch results are inconsistent, missing, or appear outdated.
- Needs a restart to fully take effect.` + Use this if Omnisearch results are inconsistent, missing, or appear outdated.
+ ${needsARestart}` }) new Setting(containerEl) .setName('Clear cache data') @@ -417,6 +458,7 @@ export const DEFAULT_SETTINGS: OmnisearchSettings = { PDFIndexing: false, imagesIndexing: false, splitCamelCase: false, + openInNewPane: false, ribbonIcon: true, showExcerpt: true, @@ -446,3 +488,7 @@ export async function loadSettings(plugin: Plugin): Promise { export async function saveSettings(plugin: Plugin): Promise { await plugin.saveData(settings) } + +export function isPluginDisabled(): boolean { + return app.loadLocalStorage(K_DISABLE_OMNISEARCH) == '1' +} \ No newline at end of file diff --git a/src/tools/notes.ts b/src/tools/notes.ts index 3c02ed4..746cd16 100644 --- a/src/tools/notes.ts +++ b/src/tools/notes.ts @@ -52,7 +52,7 @@ export async function createNote(name: string, newLeaf = false): Promise { let pathPrefix: string switch (app.vault.getConfig('newFileLocation')) { case 'current': - pathPrefix = (app.workspace.getActiveFile()?.parent.path ?? '') + '/' + pathPrefix = (app.workspace.getActiveFile()?.parent?.path ?? '') + '/' break case 'folder': pathPrefix = app.vault.getConfig('newFileFolderPath') + '/' diff --git a/src/tools/utils.ts b/src/tools/utils.ts index 8ab8131..6977db4 100644 --- a/src/tools/utils.ts +++ b/src/tools/utils.ts @@ -33,7 +33,7 @@ export function highlighterGroups(...args: any[]) { args[2] !== null && args[2] !== undefined ) - return `${args[1]}${args[2]}` + return `${args[1]}${args[2]}` return '<no content>' } diff --git a/src/typings/types-obsidian.d.ts b/src/typings/types-obsidian.d.ts index ce997a3..e12dc9d 100644 --- a/src/typings/types-obsidian.d.ts +++ b/src/typings/types-obsidian.d.ts @@ -17,7 +17,7 @@ declare module 'obsidian' { interface App { appId: string + loadLocalStorage(key: string): string | null + saveLocalStorage(key: string, value?: string): void } } - - diff --git a/versions.json b/versions.json index da50ce2..20bcba2 100644 --- a/versions.json +++ b/versions.json @@ -107,6 +107,5 @@ "1.14.0-beta.1": "1.0.0", "1.14.0": "1.0.0", "1.14.1-beta.1": "1.0.0", - "1.14.1-beta.2": "1.0.0", - "1.14.1-beta.3": "1.0.0" + "1.14.1-beta.2": "1.0.0" } \ No newline at end of file