Merge branch 'develop'
# Conflicts: # manifest-beta.json # versions.json
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
}
|
||||
|
||||
.omnisearch-result__title {
|
||||
white-space: pre-wrap;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": {
|
||||
|
||||
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
@@ -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
|
||||
|
||||
@@ -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)
|
||||
</script>
|
||||
|
||||
<div class="omnisearch-input-container">
|
||||
<div class="omnisearch-input-field">
|
||||
<input
|
||||
bind:this="{elInput}"
|
||||
bind:value
|
||||
bind:value="{value}"
|
||||
class="prompt-input"
|
||||
use:selectInput
|
||||
on:compositionend="{_ => toggleInputComposition(false)}"
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
import * as NotesIndex from '../notes-index'
|
||||
import { cacheManager } from '../cache-manager'
|
||||
import { searchEngine } from 'src/search/omnisearch'
|
||||
import { cancelable, CancelablePromise } from 'cancelable-promise'
|
||||
|
||||
export let modal: OmnisearchVaultModal
|
||||
export let previousQuery: string | undefined
|
||||
@@ -38,9 +39,24 @@
|
||||
let indexingStepDesc = ''
|
||||
let searching = true
|
||||
let refInput: InputSearch | undefined
|
||||
let openInNewPaneKey: string
|
||||
let openInCurrentPaneKey: string
|
||||
let createInNewPaneKey: string
|
||||
let createInCurrentPaneKey: string
|
||||
|
||||
$: selectedNote = resultNotes[selectedIndex]
|
||||
$: searchQuery = searchQuery ?? previousQuery
|
||||
$: if (settings.openInNewPane) {
|
||||
openInNewPaneKey = '↵'
|
||||
openInCurrentPaneKey = getCtrlKeyLabel() + ' ↵'
|
||||
createInNewPaneKey = 'shift ↵'
|
||||
createInCurrentPaneKey = getCtrlKeyLabel() + ' shift ↵'
|
||||
} else {
|
||||
openInNewPaneKey = getCtrlKeyLabel() + ' ↵'
|
||||
openInCurrentPaneKey = '↵'
|
||||
createInNewPaneKey = getCtrlKeyLabel() + ' shift ↵'
|
||||
createInCurrentPaneKey = 'shift ↵'
|
||||
}
|
||||
$: if (searchQuery) {
|
||||
searching = true
|
||||
updateResults().then(() => {
|
||||
@@ -112,9 +128,20 @@
|
||||
refInput?.setInputValue(searchQuery)
|
||||
}
|
||||
|
||||
let cancelableQuery: CancelablePromise<ResultNote[]> | 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 @@
|
||||
<span>to cycle history</span>
|
||||
</div>
|
||||
<div class="prompt-instruction">
|
||||
<span class="prompt-instruction-command">↵</span><span>to open</span>
|
||||
<span class="prompt-instruction-command">{openInCurrentPaneKey}</span><span>to open</span>
|
||||
</div>
|
||||
<div class="prompt-instruction">
|
||||
<span class="prompt-instruction-command">tab</span>
|
||||
@@ -305,15 +332,15 @@
|
||||
</div>
|
||||
|
||||
<div class="prompt-instruction">
|
||||
<span class="prompt-instruction-command">{getCtrlKeyLabel()} ↵</span>
|
||||
<span class="prompt-instruction-command">{openInNewPaneKey}</span>
|
||||
<span>to open in a new pane</span>
|
||||
</div>
|
||||
<div class="prompt-instruction">
|
||||
<span class="prompt-instruction-command">shift ↵</span>
|
||||
<span class="prompt-instruction-command">{createInCurrentPaneKey}</span>
|
||||
<span>to create</span>
|
||||
</div>
|
||||
<div class="prompt-instruction">
|
||||
<span class="prompt-instruction-command">ctrl shift ↵</span>
|
||||
<span class="prompt-instruction-command">{createInNewPaneKey}</span>
|
||||
<span>to create in a new pane</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -71,8 +71,9 @@
|
||||
<span class="omnisearch-result__title">
|
||||
<span bind:this="{elFilePathIcon}"></span>
|
||||
<span>{@html title.replace(reg, highlighterGroups)}</span>
|
||||
<span class="omnisearch-result__extension"
|
||||
>.{getExtension(note.path)}</span>
|
||||
<span class="omnisearch-result__extension">
|
||||
.{getExtension(note.path)}
|
||||
</span>
|
||||
|
||||
<!-- Counter -->
|
||||
{#if note.matches.length > 0}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
10
src/main.ts
10
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<void> {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 =>
|
||||
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 []
|
||||
}
|
||||
|
||||
@@ -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 = `<strong style="color: var(--text-accent)">Needs a restart to fully take effect.</strong>`
|
||||
|
||||
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 = `<strong style="color: var(--text-accent)">⚠️ OMNISEARCH IS DISABLED ⚠️</strong>`
|
||||
}
|
||||
|
||||
// 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: "<code>txt org</code>".<br />
|
||||
⚠️ <span style="color: var(--text-accent)">Using extensions of non-plaintext files (like .docx or .pptx) WILL cause crashes,
|
||||
because Omnisearch will try to index their content.</span><br />
|
||||
<strong style="color: var(--text-accent)">Needs a restart to fully take effect.</strong>`
|
||||
${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".<br/>
|
||||
⚠️ <span style="color: var(--text-accent)">You probably should <strong>NOT</strong> disable this.</span><br>
|
||||
⚠️ <span style="color: var(--text-accent)">Changing this setting will clear the cache.</span><br>
|
||||
<strong style="color: var(--text-accent)">Needs a restart to fully take effect.</strong>
|
||||
${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.<br/>
|
||||
⚠️ <span style="color: var(--text-accent)">Changing this setting will clear the cache.</span><br>
|
||||
<strong style="color: var(--text-accent)">Needs a restart to fully take effect.</strong>
|
||||
${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()
|
||||
|
||||
const disableDesc = new DocumentFragment()
|
||||
disableDesc.createSpan({}, span => {
|
||||
span.innerHTML = `Disable Omnisearch on this device only.<br>
|
||||
${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.<br>
|
||||
<strong style="color: var(--text-accent)">Needs a restart to fully take effect.</strong>`
|
||||
${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<void> {
|
||||
export async function saveSettings(plugin: Plugin): Promise<void> {
|
||||
await plugin.saveData(settings)
|
||||
}
|
||||
|
||||
export function isPluginDisabled(): boolean {
|
||||
return app.loadLocalStorage(K_DISABLE_OMNISEARCH) == '1'
|
||||
}
|
||||
@@ -52,7 +52,7 @@ export async function createNote(name: string, newLeaf = false): Promise<void> {
|
||||
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') + '/'
|
||||
|
||||
@@ -33,7 +33,7 @@ export function highlighterGroups(...args: any[]) {
|
||||
args[2] !== null &&
|
||||
args[2] !== undefined
|
||||
)
|
||||
return `${args[1]}<span class="${highlightClass}">${args[2]}</span>`
|
||||
return `<span>${args[1]}</span><span class="${highlightClass}">${args[2]}</span>`
|
||||
return '<no content>'
|
||||
}
|
||||
|
||||
|
||||
4
src/typings/types-obsidian.d.ts
vendored
4
src/typings/types-obsidian.d.ts
vendored
@@ -17,7 +17,7 @@ declare module 'obsidian' {
|
||||
|
||||
interface App {
|
||||
appId: string
|
||||
loadLocalStorage(key: string): string | null
|
||||
saveLocalStorage(key: string, value?: string): void
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
Reference in New Issue
Block a user