obsidian-qdrant/main.ts
Nicholai 92f49f4bf7 Summary
 What's Working:
Plugin loads successfully in Obsidian
Settings are being saved correctly to disk
Qdrant server is accessible and responding
Ollama is set up with the embedding model
UUID generation fixed for Qdrant compatibility
 Main Issue:
Plugin is using default localhost:6333 URL instead of your saved https://vectors.biohazardvfx.com URL
This is a settings initialization timing problem
🎯 Next Step:
Fix the IndexingOrchestrator to use the loaded settings instead of defaults
This is likely a simple fix - the orchestrator needs to reference this.settings that were loaded from data.json
Progress: ~95% complete - just need to fix this one settings issue and then test the full indexing + search workflow!
2025-10-23 09:24:03 -06:00

221 lines
6.1 KiB
TypeScript

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() {
console.log('Qdrant Semantic Search plugin loading...');
try {
await this.loadSettings();
console.log('Settings loaded successfully');
// Validate settings
const errors = validateSettings(this.settings);
if (errors.length > 0) {
console.warn('Settings validation warnings:', errors);
new Notice('Qdrant: Please configure settings. Settings validation warnings: ' + errors.join(', '));
}
// Add status bar item first
this.setupStatusBar();
console.log('Status bar added');
// Add commands
this.addCommands();
console.log('Commands registered');
// Add settings tab
this.addSettingTab(new QdrantSettingsTab(this.app, this));
console.log('Settings tab added');
// Initialize indexing orchestrator (non-blocking)
this.initializeOrchestrator();
console.log('Qdrant Semantic Search plugin loaded successfully');
new Notice('Qdrant Semantic Search loaded! Configure settings before indexing.');
} catch (error) {
console.error('Failed to load Qdrant plugin:', error);
new Notice('Failed to load Qdrant plugin: ' + (error as Error).message);
throw error;
}
}
private async initializeOrchestrator() {
try {
console.log('Initializing indexing orchestrator...');
this.indexingOrchestrator = new IndexingOrchestrator(this.app, this.settings);
await this.indexingOrchestrator.initialize();
// Set up progress tracking
this.setupProgressTracking();
console.log('Indexing orchestrator initialized successfully');
this.updateStatusBar('Ready');
} catch (error) {
console.error('Failed to initialize indexing orchestrator:', error);
console.error('Stack trace:', (error as Error).stack);
new Notice('Qdrant: Indexing system not ready. Please check settings and connection.');
this.updateStatusBar('Not configured');
}
}
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<boolean> {
if (!this.indexingOrchestrator) return false;
const connections = await this.indexingOrchestrator.testConnections();
return connections.qdrant;
}
async testOllamaConnection(): Promise<boolean> {
if (!this.indexingOrchestrator) return false;
const connections = await this.indexingOrchestrator.testConnections();
return connections.embedding;
}
async indexFullVault(): Promise<void> {
if (!this.indexingOrchestrator?.isReady()) {
throw new Error('Indexing system not ready');
}
await this.indexingOrchestrator.indexFullVault();
}
async indexFile(file: TFile): Promise<void> {
if (!this.indexingOrchestrator?.isReady()) {
throw new Error('Indexing system not ready');
}
await this.indexingOrchestrator.indexFile(file);
}
async clearIndex(): Promise<void> {
if (!this.indexingOrchestrator?.isReady()) {
throw new Error('Indexing system not ready');
}
await this.indexingOrchestrator.clearIndex();
}
async getIndexStats(): Promise<any> {
if (!this.indexingOrchestrator?.isReady()) {
throw new Error('Indexing system not ready');
}
return await this.indexingOrchestrator.getIndexStats();
}
}