import { App, Notice, Plugin, TFile } from 'obsidian'; import { PluginSettings, IndexingProgress } from './src/types'; import { DEFAULT_SETTINGS, validateSettings } from './src/settings'; import { IndexingOrchestrator } from './src/indexing/orchestrator'; import { SearchModal } from './src/search/searchModal'; import { QdrantSettingsTab } from './src/ui/settingsTab'; export default class QdrantPlugin extends Plugin { settings: PluginSettings; private indexingOrchestrator: IndexingOrchestrator | null = null; private statusBarItem: HTMLElement | null = null; async onload() { await this.loadSettings(); // Validate settings const errors = validateSettings(this.settings); if (errors.length > 0) { new Notice('Settings validation failed: ' + errors.join(', ')); } // Initialize indexing orchestrator try { this.indexingOrchestrator = new IndexingOrchestrator(this.app, this.settings); await this.indexingOrchestrator.initialize(); } catch (error) { console.error('Failed to initialize indexing orchestrator:', error); new Notice('Failed to initialize indexing system: ' + error.message); } // Add status bar item this.setupStatusBar(); // Add commands this.addCommands(); // Add settings tab this.addSettingTab(new QdrantSettingsTab(this.app, this)); // Set up progress tracking this.setupProgressTracking(); console.log('Qdrant Semantic Search plugin loaded'); } onunload() { // Shutdown indexing orchestrator if (this.indexingOrchestrator) { this.indexingOrchestrator.shutdown(); } } async loadSettings() { this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); } async saveSettings() { await this.saveData(this.settings); } private setupStatusBar() { this.statusBarItem = this.addStatusBarItem(); this.updateStatusBar('Ready'); } private addCommands() { // Semantic search command this.addCommand({ id: 'semantic-search', name: 'Semantic search', callback: () => { if (!this.indexingOrchestrator?.isReady()) { new Notice('Indexing system not ready. Please check your settings.'); return; } new SearchModal(this.app, this.settings).open(); } }); // Index current file command this.addCommand({ id: 'index-current-file', name: 'Index current file', checkCallback: (checking: boolean) => { const activeFile = this.app.workspace.getActiveFile(); if (activeFile instanceof TFile) { if (!checking) { this.indexFile(activeFile); } return true; } return false; } }); // Full reindex command this.addCommand({ id: 'full-reindex', name: 'Full reindex vault', callback: () => { this.indexFullVault(); } }); // Clear index command this.addCommand({ id: 'clear-index', name: 'Clear index', callback: () => { this.clearIndex(); } }); // Open graph view command if (this.settings.enableGraphView) { this.addCommand({ id: 'open-graph-view', name: 'Open graph view', callback: () => { // TODO: Implement graph view new Notice('Graph view not yet implemented'); } }); } } private setupProgressTracking() { if (!this.indexingOrchestrator) return; this.indexingOrchestrator.setProgressCallback((progress: IndexingProgress) => { this.updateStatusBar(progress); }); this.indexingOrchestrator.setErrorCallback((error: string) => { new Notice('Indexing error: ' + error); }); } private updateStatusBar(progress: IndexingProgress | string) { if (!this.statusBarItem) return; if (typeof progress === 'string') { this.statusBarItem.setText(`Qdrant: ${progress}`); return; } if (progress.isRunning) { const percentage = progress.totalFiles > 0 ? Math.round((progress.processedFiles / progress.totalFiles) * 100) : 0; this.statusBarItem.setText(`Qdrant: Indexing ${percentage}% (${progress.processedFiles}/${progress.totalFiles})`); } else { this.statusBarItem.setText('Qdrant: Ready'); } } // Public methods for settings tab async testQdrantConnection(): Promise { if (!this.indexingOrchestrator) return false; const connections = await this.indexingOrchestrator.testConnections(); return connections.qdrant; } async testOllamaConnection(): Promise { if (!this.indexingOrchestrator) return false; const connections = await this.indexingOrchestrator.testConnections(); return connections.embedding; } async indexFullVault(): Promise { if (!this.indexingOrchestrator?.isReady()) { throw new Error('Indexing system not ready'); } await this.indexingOrchestrator.indexFullVault(); } async indexFile(file: TFile): Promise { if (!this.indexingOrchestrator?.isReady()) { throw new Error('Indexing system not ready'); } await this.indexingOrchestrator.indexFile(file); } async clearIndex(): Promise { if (!this.indexingOrchestrator?.isReady()) { throw new Error('Indexing system not ready'); } await this.indexingOrchestrator.clearIndex(); } async getIndexStats(): Promise { if (!this.indexingOrchestrator?.isReady()) { throw new Error('Indexing system not ready'); } return await this.indexingOrchestrator.getIndexStats(); } }