264 lines
9.3 KiB
TypeScript
264 lines
9.3 KiB
TypeScript
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'; // Путь к твоему новому 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(); // Перерисовать настройки для обновления списка
|
||
});
|
||
});
|
||
}
|
||
}
|