bug fixes for correct loading of chat and graph panels

This commit is contained in:
dimitrievgs 2025-09-16 01:38:27 +03:00
parent 2bf5763eb1
commit 8bf1394e6f
4 changed files with 128 additions and 141 deletions

19
main.js

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,6 @@ export class HistoryPanel {
container: HTMLElement;
props: {
graphs: any[];
onGraphSelect: (data: { graphId: string; currentNode: string | null }) => void;
eventBus: EventBus;
};
graphs: any[];
@ -23,7 +22,6 @@ export class HistoryPanel {
container: HTMLElement,
props: {
graphs: any[];
onGraphSelect: (data: { graphId: string; currentNode: string | null }) => void;
eventBus: EventBus;
}
) {
@ -103,8 +101,8 @@ export class HistoryPanel {
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.onGraphSelect) {
this.props.onGraphSelect({ graphId: graphId, currentNode: null });
if (graphId) {
this.props.eventBus.emit('graph-selected', { graphId: graphId, currentNode: null });
}
}
});

View File

@ -55,10 +55,10 @@ export class ChatView extends ItemView {
});
// Подписываемся на события
this.plugin.eventBus.on('node-selected', this.handleNodeSelected.bind(this));
this.plugin.eventBus.on('new-chat', this.handleNewChat.bind(this));
this.plugin.eventBus.on('graph-selected', this.handleGraphSelected.bind(this));
}
this.plugin.eventBus.on('node-selected', this.handleNodeSelected.bind(this));
this.plugin.eventBus.on('new-chat', this.handleNewChat.bind(this));
this.plugin.eventBus.on('graph-selected', this.handleGraphSelected.bind(this));
}
async onClose(): Promise<void> {
if (this.chatPanel) {
@ -72,133 +72,130 @@ export class ChatView extends ItemView {
// ----------------------------------------------- API Methods ---------------------------------------------------------------
// TODO: проблемное место
/**
* Загружает историю сообщений от корня до указанного узла графа.
* Обновляет `chatHistory` и отображение в `chatPanel`.
* @param {string} nodeId - ID конечного узла для загрузки истории.
* @returns {Promise<void>}
*/
async fetchMessagesFromRootToNode(nodeId: string): Promise<void> {
try {
// Убедимся, что selectedGraphId установлен
if (!this.selectedGraphId) {
console.warn('selectedGraphId не установлен. Невозможно загрузить сообщения.');
return;
}
const response = await fetch(`${this.plugin.settings.apiBaseUrl}/messages/${this.selectedGraphId}/${nodeId}`);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Ошибка загрузки сообщений: ${response.status} - ${errorText}`);
}
const data = await response.json();
// ИСПРАВЛЕНО: Убедиться, что type передается, если это возможно
this.chatHistory = data.map((msg: any) => ({
...msg,
type: msg.type || msg.role // Используем type, если есть, иначе role как fallback
})) || [];
if (this.chatPanel) {
this.chatPanel.updateHistory(this.chatHistory);
}
} catch (error) {
console.error('Ошибка загрузки сообщений:', error);
// Optionally: show a notice to the user
// new Notice(`Ошибка загрузки сообщений: ${error.message}`);
}
}
// TODO: проблемное место
/**
* Загружает историю сообщений от корня до указанного узла графа.
* Обновляет `chatHistory` и отображение в `chatPanel`.
* @param {string} nodeId - ID конечного узла для загрузки истории.
* @returns {Promise<void>}
*/
async fetchMessagesFromRootToNode(nodeId: string | null): Promise<void> {
try {
// Убедимся, что selectedGraphId установлен
if (!this.selectedGraphId) {
console.warn('selectedGraphId не установлен. Невозможно загрузить сообщения.');
return;
}
const url = nodeId
? `${this.plugin.settings.apiBaseUrl}/messages/${this.selectedGraphId}/${nodeId}`
: `${this.plugin.settings.apiBaseUrl}/messages/${this.selectedGraphId}/last`;
const response = await fetch(url);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Ошибка загрузки сообщений: ${response.status} - ${errorText}`);
}
const data = await response.json();
// ИСПРАВЛЕНО: Убедиться, что type передается, если это возможно
this.chatHistory =
data.map((msg: any) => ({
...msg,
type: msg.type || msg.role // Используем type, если есть, иначе role как fallback
})) || [];
if (this.chatPanel) {
this.chatPanel.updateHistory(this.chatHistory);
}
} catch (error) {
console.error('Ошибка загрузки сообщений:', error);
// Optionally: show a notice to the user
// new Notice(`Ошибка загрузки сообщений: ${error.message}`);
}
}
// ----------------------------------------------- Event Handlers ---------------------------------------------------------------
async handleSendMessage(message: string): Promise<void> {
try {
const response = await fetch(`${this.plugin.settings.apiBaseUrl}/chat`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: message,
graph_id: this.selectedGraphId,
parent_node_id: this.currentNode,
}),
});
async handleSendMessage(message: string): Promise<void> {
try {
const response = await fetch(`${this.plugin.settings.apiBaseUrl}/chat`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
message: message,
graph_id: this.selectedGraphId,
parent_node_id: this.currentNode
})
});
if (!response.ok) {
// Пытаемся прочитать ошибку из тела ответа, если это возможно
const errorData = await response.json().catch(() => ({ message: response.statusText }));
throw new Error(`HTTP ошибка при отправке сообщения! Статус: ${response.status} - ${errorData.message}`);
}
if (!response.ok) {
// Пытаемся прочитать ошибку из тела ответа, если это возможно
const errorData = await response.json().catch(() => ({ message: response.statusText }));
throw new Error(`HTTP ошибка при отправке сообщения! Статус: ${response.status} - ${errorData.message}`);
}
const result = await response.json();
// Обновляем graph_id и current_node_id всегда
this.selectedGraphId = result.graph_id;
const newCurrentNodeId = result.graph_visualization_data.current_node_id;
this.currentNode = newCurrentNodeId;
const result = await response.json();
// Сначала обновим GraphView, чтобы он запросил последнюю версию графа
// Это предотвратит состояние гонки, когда разные запросы возвращают частичные графы.
// Вместо того, чтобы отправлять частичные данные, мы запрашиваем полный граф у бэкенда.
this.plugin.eventBus.emit('graph-selected', { graphId: this.selectedGraphId, currentNode: this.currentNode });
// Затем обновим ChatView, загрузив историю до нового current_node_id
if (newCurrentNodeId) {
await this.fetchMessagesFromRootToNode(newCurrentNodeId);
} else if (this.selectedGraphId) {
// Если newCurrentNodeId по какой-то причине не вернулся, но graph_id есть,
// пытаемся загрузить историю по последнему известному узлу или просто весь граф (чтобы отобразить пустую историю, если граф новый).
// В этом случае, если current_node_id отсутствует, historyPanel должна очиститься.
this.currentNode = null; // Сбрасываем, если no current_node_id
this.fetchMessagesFromRootToNode(''); // Передаем пустую строку или null, чтобы очистить историю
}
// Обновляем graph_id и current_node_id всегда
this.selectedGraphId = result.graph_id;
const newCurrentNodeId = result.graph_visualization_data.current_node_id;
this.currentNode = newCurrentNodeId;
} catch (error) {
console.error('Ошибка отправки сообщения:', error);
}
}
// Сначала обновим GraphView, чтобы он запросил последнюю версию графа
// Это предотвратит состояние гонки, когда разные запросы возвращают частичные графы.
// Вместо того, чтобы отправлять частичные данные, мы запрашиваем полный граф у бэкенда.
this.plugin.eventBus.emit('graph-selected', { graphId: this.selectedGraphId, currentNode: this.currentNode });
/**
* Обрабатывает выбор узла на графе. Загружает историю сообщений до выбранного узла
* и обновляет панель чата.
* @param {{ nodeId: string }} data - Объект, содержащий ID выбранного узла.
*/
handleNodeSelected(data: { nodeId: string }): void {
this.currentNode = data.nodeId;
if (this.selectedGraphId) {
this.fetchMessagesFromRootToNode(data.nodeId);
}
}
// Затем обновим ChatView, загрузив историю до нового current_node_id
if (newCurrentNodeId) {
await this.fetchMessagesFromRootToNode(newCurrentNodeId);
} else if (this.selectedGraphId) {
// Если newCurrentNodeId по какой-то причине не вернулся, но graph_id есть,
// пытаемся загрузить историю по последнему известному узлу или просто весь граф (чтобы отобразить пустую историю, если граф новый).
// В этом случае, если current_node_id отсутствует, historyPanel должна очиститься.
this.currentNode = null; // Сбрасываем, если no current_node_id
this.fetchMessagesFromRootToNode(''); // Передаем пустую строку или null, чтобы очистить историю
}
} catch (error) {
console.error('Ошибка отправки сообщения:', error);
}
}
/**
* Обрабатывает событие начала нового чата. Сбрасывает текущее состояние чата
* и очищает отображение истории.
*/
handleNewChat(): void {
this.chatHistory = [];
this.currentNode = null;
this.selectedGraphId = null;
if (this.chatPanel) {
this.chatPanel.updateHistory(this.chatHistory);
}
}
/**
* Обрабатывает выбор узла на графе. Загружает историю сообщений до выбранного узла
* и обновляет панель чата.
* @param {{ nodeId: string }} data - Объект, содержащий ID выбранного узла.
*/
handleNodeSelected(data: { nodeId: string }): void {
this.currentNode = data.nodeId;
if (this.selectedGraphId) {
this.fetchMessagesFromRootToNode(data.nodeId);
}
}
/**
* Обрабатывает выбор графа из `GraphView`. Устанавливает выбранный граф и обновляет
* историю чата, загружая сообщения, относящиеся к этому графу.
* @param {{ graphId: string; currentNode: string | null }} data - Объект, содержащий ID выбранного графа и текущий узел.
* @returns {Promise<void>}
*/
async handleGraphSelected(data: { graphId: string; currentNode: string | null }): Promise<void> {
this.selectedGraphId = data.graphId;
this.currentNode = data.currentNode;
if (this.currentNode) {
await this.fetchMessagesFromRootToNode(this.currentNode);
} else {
// Если текущего узла нет (например, пустой граф), очищаем историю
this.chatHistory = [];
if (this.chatPanel) {
this.chatPanel.updateHistory(this.chatHistory);
}
}
}
/**
* Обрабатывает событие начала нового чата. Сбрасывает текущее состояние чата
* и очищает отображение истории.
*/
handleNewChat(): void {
this.chatHistory = [];
this.currentNode = null;
this.selectedGraphId = null;
if (this.chatPanel) {
this.chatPanel.updateHistory(this.chatHistory);
}
}
/**
* Обрабатывает выбор графа из `GraphView`. Устанавливает выбранный граф и обновляет
* историю чата, загружая сообщения, относящиеся к этому графу.
* @param {{ graphId: string; currentNode: string | null }} data - Объект, содержащий ID выбранного графа и текущий узел.
* @returns {Promise<void>}
*/
async handleGraphSelected(data: { graphId: string; currentNode: string | null }): Promise<void> {
this.selectedGraphId = data.graphId;
this.currentNode = data.currentNode; // Может быть пустым, но это не проблема
await this.fetchMessagesFromRootToNode(data.currentNode);
}
}

View File

@ -99,7 +99,6 @@ export class GraphView extends ItemView {
// Инициализируем панель истории
this.historyPanel = new HistoryPanel(this.historyContainer, {
graphs: this.graphsList,
onGraphSelect: this.handleGraphSelected.bind(this),
eventBus: this.plugin.eventBus
});