import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting, ItemView, WorkspaceLeaf } from 'obsidian'; import { EventBus } from './src/utils/EventBus'; import { GraphView, GRAPH_VIEW_TYPE } from './src/views/GraphView'; import { ChatView, CHAT_VIEW_TYPE } from './src/views/ChatView'; import { WebSocketService } from './src/utils/WebSocketService'; import 'src/css/styles.css'; // ----------------------------------------------- Settings --------------------------------------------------------------- interface LLMAgentSettings { apiBaseUrl: string; systemPrompt: string; defaultModel: string; promptsFolder: string; // Новое поле enableFileReferences: boolean; // Новое поле enablePromptReferences: boolean; // Новое поле excludedFolders: string[]; // Новое поле: Список папок для исключения из автокомплита } const DEFAULT_SETTINGS: LLMAgentSettings = { apiBaseUrl: 'http://localhost:5000/api', systemPrompt: 'Ты полезный ИИ ассистент.', defaultModel: 'gemini-2.5-flash', promptsFolder: 'prompts', // Папка с промптами по умолчанию enableFileReferences: true, enablePromptReferences: true, excludedFolders: ['templates'] // Папка "templates" по умолчанию исключена }; // ----------------------------------------------- Main Plugin Class --------------------------------------------------------------- export default class LLMAgentPlugin extends Plugin { settings: LLMAgentSettings; eventBus: EventBus; webSocketService: WebSocketService; async onload() { await this.loadSettings(); this.eventBus = new EventBus(); this.webSocketService = new WebSocketService(this.eventBus, this.settings.apiBaseUrl); this.webSocketService.connect(); // Регистрируем кастомные views this.registerView(GRAPH_VIEW_TYPE, (leaf) => new GraphView(leaf, this)); this.registerView(CHAT_VIEW_TYPE, (leaf) => new ChatView(leaf, this)); // Добавляем команды this.addCommand({ id: 'open-llm-agent-graph', name: 'Открыть граф диалогов LLM Agent', callback: () => this.activateGraphView() }); this.addCommand({ id: 'open-llm-agent-chat', name: 'Открыть чат LLM Agent', callback: () => this.activateChatView() }); this.addCommand({ id: 'new-llm-agent-chat', name: 'Новый чат LLM Agent', callback: () => this.handleNewChat() }); // Добавляем иконку в ribbon this.addRibbonIcon('brain-circuit', 'LLM Agent', () => { this.activateGraphView(); this.activateChatView(); }); console.log('LLM Agent plugin загружен'); // This adds a settings tab so the user can configure various aspects of the plugin this.addSettingTab(new SampleSettingTab(this.app, this)); } onunload() { if (this.webSocketService) { this.webSocketService.disconnect(); } console.log('LLM Agent plugin выгружен'); } // ----------------------------------------------- Settings Management --------------------------------------------------------------- async loadSettings() { this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); } async saveSettings() { await this.saveData(this.settings); } // ----------------------------------------------- View Management --------------------------------------------------------------- async activateGraphView() { const existingLeaf = this.app.workspace.getLeavesOfType(GRAPH_VIEW_TYPE)[0]; if (existingLeaf) { this.app.workspace.revealLeaf(existingLeaf); } else { await this.app.workspace.getLeaf('tab').setViewState({ type: GRAPH_VIEW_TYPE, active: true }); } } async activateChatView() { const existingLeaf = this.app.workspace.getLeavesOfType(CHAT_VIEW_TYPE)[0]; if (existingLeaf) { this.app.workspace.revealLeaf(existingLeaf); } else { const rightLeaf = this.app.workspace.getRightLeaf(false); if (rightLeaf) { await rightLeaf.setViewState({ type: CHAT_VIEW_TYPE, active: true }); } } } // ----------------------------------------------- Event Handlers --------------------------------------------------------------- handleNewChat() { this.eventBus.emit('new-chat'); } } // ----------------------------------------------- Sample Setting Tab --------------------------------------------------------------- class SampleSettingTab extends PluginSettingTab { plugin: LLMAgentPlugin; constructor(app: App, plugin: LLMAgentPlugin) { super(app, plugin); this.plugin = plugin; } display(): void { const { containerEl } = this; containerEl.empty(); new Setting(containerEl) .setName('API Base URL') .setDesc('Базовый URL для API LLM Agent') .addText((text) => text .setPlaceholder('Введите URL') .setValue(this.plugin.settings.apiBaseUrl) .onChange(async (value) => { this.plugin.settings.apiBaseUrl = value; await this.plugin.saveSettings(); }) ); // Новое поле для системного промпта new Setting(containerEl) .setName('Системный промпт') .setDesc('Этот промпт будет отправляться LLM перед каждым диалогом.') .addTextArea((text) => text .setPlaceholder('Ты полезный ИИ ассистент.') .setValue(this.plugin.settings.systemPrompt) .onChange(async (value) => { this.plugin.settings.systemPrompt = value; await this.plugin.saveSettings(); }) ); // Новое поле для папки промптов new Setting(containerEl) .setName('Папка с промптами') .setDesc('Путь к папке, содержащей промпты для команд #/##') .addText((text) => text .setPlaceholder('prompts') .setValue(this.plugin.settings.promptsFolder) .onChange(async (value) => { this.plugin.settings.promptsFolder = value; await this.plugin.saveSettings(); }) ); // Настройки ссылок на файлы new Setting(containerEl) .setName('Включить ссылки на файлы') .setDesc('Разрешить использование @/@@ ссылок на файлы в хранилище') .addToggle((toggle) => toggle.setValue(this.plugin.settings.enableFileReferences).onChange(async (value) => { this.plugin.settings.enableFileReferences = value; await this.plugin.saveSettings(); }) ); // Настройки ссылок на промпты new Setting(containerEl) .setName('Включить ссылки на промпты') .setDesc('Разрешить использование #/## ссылок на промпты') .addToggle((toggle) => toggle.setValue(this.plugin.settings.enablePromptReferences).onChange(async (value) => { this.plugin.settings.enablePromptReferences = value; await this.plugin.saveSettings(); }) ); // Новое поле для настройки исключенных папок new Setting(containerEl) .setName('Исключенные папки для ссылок') .setDesc('Список относительных путей к папкам, файлы из которых не будут предлагаться в автодополнении ссылок (@/@@).') .addToggle((toggle) => toggle.setValue(this.plugin.settings.enableFileReferences).onChange(async (value) => { this.plugin.settings.enableFileReferences = value; await this.plugin.saveSettings(); }) ); // Динамический список исключенных папок this.plugin.settings.excludedFolders.forEach((folder, index) => { new Setting(containerEl) .setClass('llm-agent-excluded-folder-item') // Добавляем класс для стилизации .addText((text) => { text.setPlaceholder('Путь к папке (например, templates/)' + index) .setValue(folder) .onChange(async (value) => { this.plugin.settings.excludedFolders[index] = value; await this.plugin.saveSettings(); // Очищаем кэш автокомплита при изменении настроек this.plugin.eventBus.emit('settings-changed'); }); }) .addExtraButton((button) => { button .setIcon('x') .setTooltip('Удалить папку') .onClick(async () => { this.plugin.settings.excludedFolders.splice(index, 1); await this.plugin.saveSettings(); this.display(); // Перерисовать настройки для обновления списка // Очищаем кэш автокомплита при изменении настроек this.plugin.eventBus.emit('settings-changed'); }); }); }); new Setting(containerEl).addButton((button) => { button .setButtonText('Добавить папку') .setIcon('plus') .onClick(async () => { this.plugin.settings.excludedFolders.push(''); // Добавляем пустое поле await this.plugin.saveSettings(); this.display(); // Перерисовать настройки для обновления списка }); }); } }