llm-agent-plugin/src/components/HistoryPanel.ts
2025-09-24 02:29:11 +03:00

157 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Управляет и отображает историю графов чата. Предоставляет функционал для выбора
* прошлых разговоров, начала новых чатов и удаления записей графов.
*/
import { EventBus } from '../utils/EventBus';
export class HistoryPanel {
container: HTMLElement;
props: {
graphs: any[];
eventBus: EventBus;
};
graphs: any[];
newChatButton: HTMLButtonElement;
chatList: HTMLElement;
contextMenu: HTMLElement;
deleteButton: HTMLButtonElement;
contextMenuGraphId: string | null;
constructor(
container: HTMLElement,
props: {
graphs: any[];
eventBus: EventBus;
}
) {
this.container = container;
this.props = props;
this.graphs = props.graphs || [];
this.init();
}
init(): void {
this.container.innerHTML = `
<div class="llm-agent-history-panel">
<div class="history-header">
<h3>История диалогов</h3>
<button id="new-chat-button" class="new-chat-button">+ Новый чат</button>
</div>
<div class="chat-list" id="chat-list"></div>
<div class="context-menu" id="context-menu" style="display: none;">
<button id="delete-graph-button">Удалить</button>
</div>
</div>
`;
this.newChatButton = this.container.querySelector('#new-chat-button') as HTMLButtonElement;
this.chatList = this.container.querySelector('#chat-list') as HTMLElement;
this.contextMenu = this.container.querySelector('#context-menu') as HTMLElement;
this.deleteButton = this.container.querySelector('#delete-graph-button') as HTMLButtonElement;
this.setupEventListeners();
this.renderGraphs();
}
setupEventListeners(): void {
this.newChatButton.addEventListener('click', () => {
this.props.eventBus.emit('new-chat');
});
this.deleteButton.addEventListener('click', () => {
if (this.contextMenuGraphId) {
this.handleDeleteGraph(this.contextMenuGraphId);
}
});
// Закрытие контекстного меню при клике вне его
document.addEventListener('click', (e) => {
if (!this.contextMenu.contains(e.target as Node)) {
this.contextMenu.style.display = 'none';
}
});
}
renderGraphs(): void {
this.chatList.innerHTML = this.graphs
.map((graph) => {
const titleClass = graph.title_generated ? 'graph-title-generated' : 'graph-title-pending';
const displayTitle = graph.title || graph.first_message || `Граф ${graph.id}`;
return `
<div class="chat-list-item" data-graph-id="${graph.id}">
<div class="chat-item-content">
<div class="chat-item-text">
<span class="truncate ${titleClass}">
${displayTitle}
</span>
</div>
<div class="chat-item-actions">
<button class="chat-item-options-button" data-graph-id="${graph.id}">
</button>
</div>
</div>
</div>
`;
})
.join('');
// Добавляем обработчики событий
this.chatList.querySelectorAll('.chat-list-item').forEach((item) => {
item.addEventListener('click', (e) => {
if (!(e.target as HTMLElement).classList.contains('chat-item-options-button')) {
const graphId = (item as HTMLElement).dataset.graphId;
if (graphId) {
this.props.eventBus.emit('graph-selected', { graphId: graphId, currentNode: null });
}
}
});
});
this.chatList.querySelectorAll('.chat-item-options-button').forEach((button) => {
button.addEventListener('click', (e) => {
e.stopPropagation();
this.showContextMenu(e, (button as HTMLElement).dataset.graphId || '');
});
});
}
showContextMenu(event: Event, graphId: string): void {
this.contextMenuGraphId = graphId;
this.contextMenu.style.display = 'block';
this.contextMenu.style.left = `${(event as MouseEvent).clientX}px`;
this.contextMenu.style.top = `${(event as MouseEvent).clientY}px`;
}
async handleDeleteGraph(graphId: string): Promise<void> {
try {
const response = await fetch(`http://localhost:5000/api/graphs/${graphId}`, {
method: 'DELETE'
});
if (!response.ok) {
throw new Error(`HTTP ошибка! статус: ${response.status}`);
}
// Обновляем список графов
this.graphs = this.graphs.filter((graph) => graph.id !== graphId);
this.renderGraphs();
this.contextMenu.style.display = 'none';
} catch (error) {
console.error('Ошибка удаления графа:', error);
}
}
updateGraphs(newGraphs: any[]): void {
this.graphs = newGraphs;
this.renderGraphs();
}
destroy(): void {
this.container.innerHTML = '';
}
}