Enhance Search Modal, Error State Handling in Khoj Obsidian Plugin

### Search Modal Enhancements
  - b52cd85 Allow Reranking results using Keybinding from Khoj Search Modal
  - 580f4ac Add hints to Modal for available Keybindings
  - da49ea2 Add placeholder text to modal in Khoj Obsidian plugin
  
### Handle Failure to Connect to Khoj Backend
Load plugin but warn on failure to connect to Khoj backend

- f046a95 Track connectedToBackend as a setting. Use it across obsidian plugin to:
  - Disable command if not connected to backend
  - Trigger warning notice on clicking Khoj ribbon if not connected to backend
  - Show warning at top of Khoj Obsidian plugin settings pane
- 768e874 Load obsidian plugin even if fail to connect to backend but show warning
  - Allows user to see reason for failure to try resolve it
  - Allows user to update Khoj URL settings to point to URL of Khoj server
  
### Miscellaneous
- 7991ab7 Add button in Obsidian plugin settings to force re-indexing your vault
  - Useful if index gets corrupted
This commit is contained in:
Debanjum
2023-01-10 23:20:32 -03:00
committed by GitHub
4 changed files with 90 additions and 20 deletions

View File

@@ -1,4 +1,4 @@
import { Plugin } from 'obsidian';
import { Notice, Plugin } from 'obsidian';
import { KhojSetting, KhojSettingTab, DEFAULT_SETTINGS } from 'src/settings'
import { KhojModal } from 'src/modal'
import { configureKhojBackend } from './utils';
@@ -14,18 +14,22 @@ export default class Khoj extends Plugin {
this.addCommand({
id: 'search',
name: 'Search',
callback: () => {
new KhojModal(this.app, this.settings).open();
checkCallback: (checking) => {
if (!checking && this.settings.connectedToBackend)
new KhojModal(this.app, this.settings).open();
return this.settings.connectedToBackend;
}
});
// Create an icon in the left ribbon.
this.addRibbonIcon('search', 'Khoj', (_: MouseEvent) => {
// Called when the user clicks the icon.
new KhojModal(this.app, this.settings).open();
this.settings.connectedToBackend
? new KhojModal(this.app, this.settings).open()
: new Notice(`Ensure Khoj backend is running and Khoj URL is pointing to it in the plugin settings`);
});
// Add a settings tab so the user can configure various aspects of the plugin
// Add a settings tab so the user can configure khoj
this.addSettingTab(new KhojSettingTab(this.app, this));
}
@@ -41,7 +45,7 @@ export default class Khoj extends Plugin {
}
async saveSettings() {
await this.saveData(this.settings)
.then(() => configureKhojBackend(this.settings));
await configureKhojBackend(this.settings)
.then(() => this.saveData(this.settings));
}
}

View File

@@ -1,4 +1,4 @@
import { App, SuggestModal, Notice, request, MarkdownRenderer } from 'obsidian';
import { App, SuggestModal, Notice, request, MarkdownRenderer, Instruction, Platform } from 'obsidian';
import { KhojSetting } from 'src/settings';
import { getVaultAbsolutePath } from 'src/utils';
@@ -9,16 +9,54 @@ export interface SearchResult {
export class KhojModal extends SuggestModal<SearchResult> {
setting: KhojSetting;
rerank: boolean;
constructor(app: App, setting: KhojSetting) {
super(app);
this.setting = setting;
this.rerank = false;
// Register Modal Keybindings to Rerank Results
this.scope.register(['Mod'], 'Enter', async () => {
this.rerank = true
let results = await this.getSuggestions(this.inputEl.value);
this.resultContainerEl.empty();
results.forEach((result) => {
this.renderSuggestion(result, this.resultContainerEl);
});
this.rerank = false
});
// Add Hints to Modal for available Keybindings
const modalInstructions: Instruction[] = [
{
command: '↑↓',
purpose: 'to navigate',
},
{
command: '↵',
purpose: 'to open',
},
{
command: Platform.isMacOS ? 'cmd ↵': 'ctrl ↵',
purpose: 'to rerank',
},
{
command: 'esc',
purpose: 'to dismiss',
},
]
this.setInstructions(modalInstructions);
// Set Placeholder Text for Modal
this.setPlaceholder('Search with Khoj...');
}
async getSuggestions(query: string): Promise<SearchResult[]> {
// Query Khoj backend for search results
var searchUrl = `${this.setting.khojUrl}/api/search?q=${query}&n=${this.setting.resultsCount}&t=markdown`
var results = await request(searchUrl)
let searchUrl = `${this.setting.khojUrl}/api/search?q=${query}&n=${this.setting.resultsCount}&r=${this.rerank}&t=markdown`
let results = await request(searchUrl)
.then(response => JSON.parse(response))
.then(data => {
return data.map((result: any) => {

View File

@@ -1,4 +1,4 @@
import { App, PluginSettingTab, Setting } from 'obsidian';
import { App, PluginSettingTab, request, Setting } from 'obsidian';
import Khoj from 'src/main';
import { getVaultAbsolutePath } from 'src/utils';
@@ -6,12 +6,14 @@ export interface KhojSetting {
resultsCount: number;
khojUrl: string;
obsidianVaultPath: string;
connectedToBackend: boolean;
}
export const DEFAULT_SETTINGS: KhojSetting = {
resultsCount: 6,
khojUrl: 'http://localhost:8000',
obsidianVaultPath: getVaultAbsolutePath()
obsidianVaultPath: getVaultAbsolutePath(),
connectedToBackend: false,
}
export class KhojSettingTab extends PluginSettingTab {
@@ -25,8 +27,15 @@ export class KhojSettingTab extends PluginSettingTab {
display(): void {
const { containerEl } = this;
containerEl.empty();
// Add notice if unable to connect to khoj backend
if (!this.plugin.settings.connectedToBackend) {
containerEl.createEl('small', { text: '❗Ensure Khoj backend is running and Khoj URL is correctly set below' });
}
// Add khoj settings configurable from the plugin settings tab
new Setting(containerEl)
.setName('Vault Paths')
.setName('Vault Path')
.setDesc('The Obsidian Vault to search with Khoj')
.addText(text => text
.setValue(`${this.plugin.settings.obsidianVaultPath}`)
@@ -44,7 +53,7 @@ export class KhojSettingTab extends PluginSettingTab {
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Number of Results')
.setName('Results Count')
.setDesc('The number of search results to show')
.addText(text => text
.setPlaceholder('6')
@@ -53,5 +62,14 @@ export class KhojSettingTab extends PluginSettingTab {
this.plugin.settings.resultsCount = parseInt(value);
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Index Vault')
.setDesc('Manually force Khoj to re-index your Obsidian Vault')
.addButton(button => button
.setButtonText('Update')
.onClick(async () => {
await request(`${this.plugin.settings.khojUrl}/api/update?t=markdown&force=true`);
}
));
}
}

View File

@@ -13,16 +13,25 @@ export async function configureKhojBackend(setting: KhojSetting) {
let mdInVault = `${setting.obsidianVaultPath}/**/*.md`;
let khojConfigUrl = `${setting.khojUrl}/api/config/data`;
// Load khoj app config from backend API, Update Markdown config and save
let khoj_configured = await request(khojConfigUrl)
.then(response => response !== "null");
// Check if khoj backend is configured, show error if backend is not running
let khoj_already_configured = await request(khojConfigUrl)
.then(response => {
setting.connectedToBackend = true;
return response !== "null"
})
.catch(error => {
setting.connectedToBackend = false;
new Notice(`Ensure Khoj backend is running and Khoj URL is pointing to it in the plugin settings.\n\n${error}`);
})
// Short-circuit configuring khoj if unable to connect to khoj backend
if (!setting.connectedToBackend) return;
// Get current config if khoj backend configured, else get default config from khoj backend
await request(khoj_configured ? khojConfigUrl : `${khojConfigUrl}/default`)
await request(khoj_already_configured ? khojConfigUrl : `${khojConfigUrl}/default`)
.then(response => JSON.parse(response))
.then(data => {
// If khoj backend not configured yet
if (!khoj_configured) {
if (!khoj_already_configured) {
// Create khoj content-type config with only markdown configured
let khojObsidianPluginPath = `${setting.obsidianVaultPath}/${this.app.vault.configDir}/plugins/khoj/`;
data["content-type"] = {
@@ -70,9 +79,10 @@ export async function configureKhojBackend(setting: KhojSetting) {
updateKhojBackend(setting.khojUrl, data);
console.log(`Khoj: Updated markdown config in khoj backend config:\n${JSON.stringify(data["content-type"]["markdown"])}`)
}
new Notice(`✅ Successfully Setup Khoj`);
})
.catch(error => {
new Notice(`Ensure Khoj backend is running and Khoj URL is correct in the Khoj plugin settings.\nError: ${error}`);
new Notice(`Failed to configure Khoj backend. Contact developer on Github. \n\nError: ${error}`);
})
}