/** * Answer Viewer UI * * UI компонент для просмотра ответов. */ import appState from '../state/appState.js' import { escapeHtml, formatTime, formatTimestamp, isTableText, parseTextTable } from '../utils/format.utils.js' import { setElementText, setElementHTML, addClass, removeClass, hideElement, showElement } from '../utils/dom.utils.js' /** * Показать просмотрщик ответов, скрыть построитель запросов */ export function show() { const queryBuilder = document.getElementById('query-builder') const answerViewer = document.getElementById('answer-viewer') if (queryBuilder) { addClass(queryBuilder, 'hidden') } if (answerViewer) { removeClass(answerViewer, 'hidden') } } /** * Отрендерить ответ по индексу * @param {number} index - Индекс ответа * @param {Function} onLoadAnnotations - Callback для загрузки аннотаций */ export function render(index, onLoadAnnotations) { const env = appState.getCurrentEnv() const answer = env.currentResponse?.answers[index] if (!answer) { console.error('Answer not found at index:', index) return } const isBackendMode = answer.backend_mode === true // Show answer viewer show() // Render question header setElementText('current-question-number', index + 1) setElementText('current-question-text', answer.question) // Render metadata setElementText('processing-time', isBackendMode ? 'N/A' : formatTime(answer.processing_time_sec)) setElementText('request-id', env.requestId || '-') setElementText('request-timestamp', env.requestTimestamp ? formatTimestamp(env.requestTimestamp) : '-') // Render answer bodies renderBody('body-research-text', answer.body_research) renderBody('body-analytical-text', answer.body_analytical_hub) // Show/hide documents sections based on mode const docsSection = document.querySelector('.answer-section:has(#docs-tabs)') if (docsSection) { if (isBackendMode) { hideElement(docsSection) } else { showElement(docsSection) } } if (!isBackendMode) { // Render documents (only in bench mode) renderDocuments('vectorstore-research-docs', answer.docs_from_vectorstore?.research, 'docs_from_vectorstore', 'research', index) renderDocuments('vectorstore-analytical-docs', answer.docs_from_vectorstore?.analytical_hub, 'docs_from_vectorstore', 'analytical_hub', index) renderDocuments('llm-research-docs', answer.docs_to_llm?.research, 'docs_to_llm', 'research', index) renderDocuments('llm-analytical-docs', answer.docs_to_llm?.analytical_hub, 'docs_to_llm', 'analytical_hub', index) } // Load annotations if (typeof onLoadAnnotations === 'function') { onLoadAnnotations(index) } } /** * Отрендерить тело ответа * @param {string} elementId - ID элемента * @param {string} text - Текст ответа */ export function renderBody(elementId, text) { const container = document.getElementById(elementId) if (!container) { console.warn(`Element ${elementId} not found`) return } if (!text) { setElementHTML(container, '

Нет данных

') return } if (isTableText(text)) { const table = parseTextTable(text) if (table) { setElementHTML(container, `
${table}
`) return } } // Render as plain text with line breaks const html = `

${escapeHtml(text).replace(/\n/g, '
')}

` setElementHTML(container, html) } /** * Отрендерить документы * @param {string} containerId - ID контейнера * @param {Array} docs - Массив документов * @param {string} section - Секция (docs_from_vectorstore, docs_to_llm) * @param {string} subsection - Подсекция (research, analytical_hub) * @param {number} answerIndex - Индекс ответа */ export function renderDocuments(containerId, docs, section, subsection, answerIndex) { const container = document.getElementById(containerId) if (!container) { console.warn(`Container ${containerId} not found`) return } if (!docs || docs.length === 0) { setElementHTML(container, `
Нет документов
`) return } const html = docs.map((doc, docIndex) => { const docId = `doc-${section}-${subsection}-${docIndex}` let docContent = '' if (typeof doc === 'string') { if (isTableText(doc)) { const table = parseTextTable(doc) docContent = table || `
${escapeHtml(doc)}
` } else { docContent = `

${escapeHtml(doc).replace(/\n/g, '
')}

` } } else { docContent = `
${escapeHtml(JSON.stringify(doc, null, 2))}
` } return `
Документ #${docIndex + 1} expand_more
${docContent}
Пометки
` }).join('') setElementHTML(container, html) } /** * Переключить раскрытие expansion panel * @param {string} id - ID панели */ export function toggleExpansion(id) { const panel = document.getElementById(id) if (panel) { panel.classList.toggle('expanded') } } // Export as default object export default { show, render, renderBody, renderDocuments, toggleExpansion }