import { Plugin, WorkspaceLeaf } from "obsidian"; import { HermesChatSettingTab, HermesChatSettings, DEFAULT_SETTINGS } from "./settings"; import { ChatView, VIEW_TYPE } from "./chat-view"; import { ApiClient } from "./api-client"; import { ContextBridge } from "./context-bridge"; import type { GatewayConfig } from "./types"; export default class HermesChatPlugin extends Plugin { settings: HermesChatSettings; private statusBarItem: HTMLElement | null = null; private apiClient: ApiClient | null = null; private contextBridge: ContextBridge | null = null; async onload() { await this.loadSettings(); // Register the chat view this.registerView( VIEW_TYPE, (leaf: WorkspaceLeaf) => { const view = new ChatView(leaf, this.apiClient!, this.contextBridge!); view.setContextActive(this.settings.enableContextInjection); return view; } ); // Add settings tab this.addSettingTab(new HermesChatSettingTab(this.app, this)); // Ribbon icon to activate chat view this.addRibbonIcon("message-square", "Open Hermes Chat", () => { this.activateView(); }); // Status bar item showing model name if (this.settings.showModelInfo) { this.statusBarItem = this.addStatusBarItem(); this.statusBarItem.setText("Hermes: connecting..."); } // Initialize API client from settings const gatewayConfig: GatewayConfig = { baseUrl: this.settings.gatewayUrl, apiKey: this.settings.apiKey, model: "hermes-agent", }; this.apiClient = new ApiClient(gatewayConfig); this.contextBridge = new ContextBridge(this.app); // Command: Open Hermes Chat this.addCommand({ id: "open-hermes-chat", name: "Open Hermes Chat", callback: () => { this.activateView(); }, }); // Command: Send Current Note as Context this.addCommand({ id: "send-note-as-context", name: "Send Current Note as Context", callback: () => { const context = this.contextBridge?.getActiveNoteContext(); if (context) { this.activateView(); // Note: actual context injection happens in chat-view's handleSend via the contextBridge console.log(`Context ready: vault=${context.vault}, path=${context.path}`); } else { console.log("No active note to send as context"); } }, }); // Check gateway connectivity if (this.statusBarItem && this.apiClient) { this.statusBarItem.setText("Hermes: checking..."); this.apiClient.healthCheck().then((healthy) => { if (this.statusBarItem) { this.statusBarItem.setText(healthy ? "Hermes: connected" : "Hermes: unreachable"); } }); } } async onunload() { // Detach the view when plugin is unloaded this.app.workspace.detachLeavesOfType(VIEW_TYPE); } async activateView() { const { workspace } = this.app; // Check if view already exists const existing = workspace.getLeavesOfType(VIEW_TYPE); if (existing.length > 0) { workspace.revealLeaf(existing[0]); return; } // Create new leaf in right sidebar const leaf = workspace.getRightLeaf(false); if (leaf) { await leaf.setViewState({ type: VIEW_TYPE, active: true }); workspace.revealLeaf(leaf); } } async loadSettings() { this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); } async saveSettings() { await this.saveData(this.settings); this.syncContextToViews(); } /** Push enableContextInjection setting to any open ChatViews. */ private syncContextToViews(): void { const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE); for (const leaf of leaves) { if (leaf.view instanceof ChatView) { leaf.view.setContextActive(this.settings.enableContextInjection); } } } }