import {
Notice,
Platform,
Plugin,
PluginSettingTab,
Setting,
SliderComponent,
} from 'obsidian'
import { writable } from 'svelte/store'
import { database } from './database'
import {
K_DISABLE_OMNISEARCH,
getTextExtractor,
isCacheEnabled,
} from './globals'
import type OmnisearchPlugin from './main'
interface WeightingSettings {
weightBasename: number
weightDirectory: number
weightH1: number
weightH2: number
weightH3: number
weightUnmarkedTags: number
}
export interface OmnisearchSettings extends WeightingSettings {
/** Enables caching to speed up indexing */
useCache: boolean
/** Respect the "excluded files" Obsidian setting by downranking results ignored files */
hideExcluded: boolean
/** Ignore diacritics when indexing files */
ignoreDiacritics: boolean
/** Extensions of plain text files to index, in addition to .md */
indexedFileTypes: string[]
/** Enable PDF indexing */
PDFIndexing: boolean
/** Enable Images indexing */
imagesIndexing: boolean
/** Enable Office documents indexing */
officeIndexing: boolean
/** Enable Excalidraw indexing */
excalidrawIndexing: boolean
/** Enable indexing of unknown files */
unsupportedFilesIndexing: 'yes' | 'no' | 'default'
/** Activate the small 🔍 button on Obsidian's ribbon */
ribbonIcon: boolean
/** Display the small contextual excerpt in search results */
showExcerpt: boolean
/** Render line returns with
in excerpts */
renderLineReturnInExcerpts: boolean
/** Enable a "create note" button in the Vault Search modal */
showCreateButton: boolean
/** Re-execute the last query when opening Omnisearch */
showPreviousQueryResults: boolean
/** Key for the welcome message when Obsidian is updated. A message is only shown once. */
welcomeMessage: string
/** If a query returns 0 result, try again with more relax conditions */
simpleSearch: boolean
highlight: boolean
splitCamelCase: boolean
openInNewPane: boolean
verboseLogging: boolean
vimLikeNavigationShortcut: boolean
fuzziness: '0' | '1' | '2'
httpApiEnabled: boolean
httpApiPort: string
httpApiNotice: boolean
}
/**
* A store to reactively toggle the `showExcerpt` setting on the fly
*/
export const showExcerpt = writable(false)
const needsARestart = `Needs a restart to fully take effect.`
export class SettingsTab extends PluginSettingTab {
plugin: OmnisearchPlugin
constructor(plugin: OmnisearchPlugin) {
super(plugin.app, plugin)
this.plugin = plugin
showExcerpt.subscribe(async v => {
settings.showExcerpt = v
await saveSettings(this.plugin)
})
}
display(): void {
const { containerEl } = this
containerEl.empty()
if (this.app.loadLocalStorage(K_DISABLE_OMNISEARCH) == '1') {
const span = containerEl.createEl('span')
span.innerHTML = `⚠️ OMNISEARCH IS DISABLED ⚠️`
}
// Settings main title
containerEl.createEl('h2', { text: 'Omnisearch' })
// Sponsor link - Thank you!
const divSponsor = containerEl.createDiv()
divSponsor.innerHTML = `
`
//#region Indexing
const indexingDesc = new DocumentFragment()
indexingDesc.createSpan({}, span => {
span.innerHTML = `⚠️ Changing indexing settings will clear the cache, and requires a restart of Obsidian.
`
if (getTextExtractor()) {
span.innerHTML += `
👍 You have installed Text Extractor, Omnisearch can use it to index PDFs and images contents.
Text extraction only works on desktop, but the cache can be synchronized with your mobile device.`
} else {
span.innerHTML += `⚠️ Omnisearch requires Text Extractor to index PDFs and images.`
}
})
new Setting(containerEl)
.setName('Indexing')
.setHeading()
.setDesc(indexingDesc)
// PDF Indexing
const indexPDFsDesc = new DocumentFragment()
indexPDFsDesc.createSpan({}, span => {
span.innerHTML = `Omnisearch will use Text Extractor to index the content of your PDFs`
})
new Setting(containerEl)
.setName(
`PDFs content indexing ${getTextExtractor() ? '' : '⚠️ Disabled'}`
)
.setDesc(indexPDFsDesc)
.addToggle(toggle =>
toggle.setValue(settings.PDFIndexing).onChange(async v => {
await database.clearCache()
settings.PDFIndexing = v
await saveSettings(this.plugin)
})
)
.setDisabled(!getTextExtractor())
// Images Indexing
const indexImagesDesc = new DocumentFragment()
indexImagesDesc.createSpan({}, span => {
span.innerHTML = `Omnisearch will use Text Extractor to OCR your images and index their content`
})
new Setting(containerEl)
.setName(`Images OCR indexing ${getTextExtractor() ? '' : '⚠️ Disabled'}`)
.setDesc(indexImagesDesc)
.addToggle(toggle =>
toggle.setValue(settings.imagesIndexing).onChange(async v => {
await database.clearCache()
settings.imagesIndexing = v
await saveSettings(this.plugin)
})
)
.setDisabled(!getTextExtractor())
// Office Documents Indexing
const indexOfficesDesc = new DocumentFragment()
indexOfficesDesc.createSpan({}, span => {
span.innerHTML = `Omnisearch will use Text Extractor to index the content of your office documents (currently
.docxand
.xlsx)` }) new Setting(containerEl) .setName( `Documents content indexing ${getTextExtractor() ? '' : '⚠️ Disabled'}` ) .setDesc(indexOfficesDesc) .addToggle(toggle => toggle.setValue(settings.officeIndexing).onChange(async v => { await database.clearCache() settings.officeIndexing = v await saveSettings(this.plugin) }) ) .setDisabled(!getTextExtractor()) // Index filenames of unsupported files const indexUnsupportedDesc = new DocumentFragment() indexUnsupportedDesc.createSpan({}, span => { span.innerHTML = ` Omnisearch can index filenames of "unsupported" files, such as e.g.
.mp4or non-extracted PDFs & images.
md files, Omnisearch can also index other PLAINTEXT files.txt org csv".shift ↵ shortcut, can be useful for mobile device users.`
})
new Setting(containerEl)
.setName('Show "Create note" button')
.setDesc(createBtnDesc)
.addToggle(toggle =>
toggle.setValue(settings.showCreateButton).onChange(async v => {
settings.showCreateButton = v
await saveSettings(this.plugin)
})
)
// Highlight results
new Setting(containerEl)
.setName('Highlight matching words in results')
.setDesc(
'Will highlight matching results when enabled. See README for more customization options.'
)
.addToggle(toggle =>
toggle.setValue(settings.highlight).onChange(async v => {
settings.highlight = v
await saveSettings(this.plugin)
})
)
//#endregion User Interface
//#region Results Weighting
new Setting(containerEl).setName('Results weighting').setHeading()
new Setting(containerEl)
.setName(
`File name & declared aliases (default: ${DEFAULT_SETTINGS.weightBasename})`
)
.addSlider(cb => this.weightSlider(cb, 'weightBasename'))
new Setting(containerEl)
.setName(`File directory (default: ${DEFAULT_SETTINGS.weightDirectory})`)
.addSlider(cb => this.weightSlider(cb, 'weightDirectory'))
new Setting(containerEl)
.setName(`Headings level 1 (default: ${DEFAULT_SETTINGS.weightH1})`)
.addSlider(cb => this.weightSlider(cb, 'weightH1'))
new Setting(containerEl)
.setName(`Headings level 2 (default: ${DEFAULT_SETTINGS.weightH2})`)
.addSlider(cb => this.weightSlider(cb, 'weightH2'))
new Setting(containerEl)
.setName(`Headings level 3 (default: ${DEFAULT_SETTINGS.weightH3})`)
.addSlider(cb => this.weightSlider(cb, 'weightH3'))
new Setting(containerEl)
.setName(
`Tags without the # (default: ${DEFAULT_SETTINGS.weightUnmarkedTags})`
)
.addSlider(cb => this.weightSlider(cb, 'weightUnmarkedTags'))
//#endregion Results Weighting
//#region Debugging
new Setting(containerEl).setName('Debugging').setHeading()
new Setting(containerEl)
.setName('Enable verbose logging')
.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
await saveSettings(this.plugin)
})
)
//#endregion Debugging
//#region HTTP Server
if (!Platform.isMobile) {
const httpServerDesc = new DocumentFragment()
httpServerDesc.createSpan({}, span => {
span.innerHTML = `Omnisearch can be used through a simple HTTP server (more information).`
})
new Setting(containerEl)
.setName('API Access Through HTTP')
.setHeading()
.setDesc(httpServerDesc)
new Setting(containerEl)
.setName('Enable the HTTP server')
.addToggle(toggle =>
toggle.setValue(settings.httpApiEnabled).onChange(async v => {
settings.httpApiEnabled = v
if (v) {
this.plugin.apiHttpServer.listen(settings.httpApiPort)
} else {
this.plugin.apiHttpServer.close()
}
await saveSettings(this.plugin)
})
)
new Setting(containerEl).setName('HTTP Port').addText(component => {
component
.setValue(settings.httpApiPort)
.setPlaceholder('51361')
.onChange(async v => {
if (parseInt(v) > 65535) {
v = settings.httpApiPort
component.setValue(settings.httpApiPort)
}
settings.httpApiPort = v
if (settings.httpApiEnabled) {
this.plugin.apiHttpServer.close()
this.plugin.apiHttpServer.listen(settings.httpApiPort)
}
await saveSettings(this.plugin)
})
})
new Setting(containerEl)
.setName('Show a notification when the server starts')
.setDesc(
'Will display a notification if the server is enabled, at Obsidian startup.'
)
.addToggle(toggle =>
toggle.setValue(settings.httpApiNotice).onChange(async v => {
settings.httpApiNotice = v
await saveSettings(this.plugin)
})
)
}
//#endregion HTTP Server
//#region Danger Zone
new Setting(containerEl).setName('Danger Zone').setHeading()
// Ignore diacritics
const diacriticsDesc = new DocumentFragment()
diacriticsDesc.createSpan({}, span => {
span.innerHTML = `Normalize diacritics in search terms. Words like "brûlée" or "žluťoučký" will be indexed as "brulee" and "zlutoucky".