245 lines
7.5 KiB
JavaScript
245 lines
7.5 KiB
JavaScript
|
|
/**
|
|||
|
|
* Annotations UI
|
|||
|
|
*
|
|||
|
|
* UI компонент для работы с аннотациями ответов.
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
import appState from '../state/appState.js'
|
|||
|
|
import { getInputValue, setInputValue } from '../utils/dom.utils.js'
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Инициализировать аннотацию для ответа
|
|||
|
|
* @param {number} index - Индекс ответа
|
|||
|
|
*/
|
|||
|
|
export function initForAnswer(index) {
|
|||
|
|
const env = appState.getCurrentEnv()
|
|||
|
|
|
|||
|
|
if (!env.annotations[index]) {
|
|||
|
|
env.annotations[index] = {
|
|||
|
|
overall: { rating: '', comment: '' },
|
|||
|
|
body_research: { issues: [], comment: '' },
|
|||
|
|
body_analytical_hub: { issues: [], comment: '' },
|
|||
|
|
docs_from_vectorstore: { research: {}, analytical_hub: {} },
|
|||
|
|
docs_to_llm: { research: {}, analytical_hub: {} }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Загрузить аннотации для ответа
|
|||
|
|
* @param {number} index - Индекс ответа
|
|||
|
|
*/
|
|||
|
|
export function loadForAnswer(index) {
|
|||
|
|
initForAnswer(index)
|
|||
|
|
|
|||
|
|
const env = appState.getCurrentEnv()
|
|||
|
|
const annotation = env.annotations[index]
|
|||
|
|
|
|||
|
|
// Load overall rating
|
|||
|
|
const ratingSelect = document.getElementById('overall-rating')
|
|||
|
|
const overallComment = document.getElementById('overall-comment')
|
|||
|
|
|
|||
|
|
if (ratingSelect) {
|
|||
|
|
ratingSelect.value = annotation.overall.rating || ''
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (overallComment) {
|
|||
|
|
setInputValue(overallComment, annotation.overall.comment || '')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Load body annotations
|
|||
|
|
loadSection('body_research', annotation.body_research)
|
|||
|
|
loadSection('body_analytical_hub', annotation.body_analytical_hub)
|
|||
|
|
|
|||
|
|
// Load document annotations
|
|||
|
|
loadDocuments('docs_from_vectorstore', 'research', annotation.docs_from_vectorstore?.research)
|
|||
|
|
loadDocuments('docs_from_vectorstore', 'analytical_hub', annotation.docs_from_vectorstore?.analytical_hub)
|
|||
|
|
loadDocuments('docs_to_llm', 'research', annotation.docs_to_llm?.research)
|
|||
|
|
loadDocuments('docs_to_llm', 'analytical_hub', annotation.docs_to_llm?.analytical_hub)
|
|||
|
|
|
|||
|
|
// Setup event listeners for current answer
|
|||
|
|
setupListeners()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Загрузить аннотацию секции (body)
|
|||
|
|
* @param {string} section - Название секции
|
|||
|
|
* @param {object} data - Данные аннотации
|
|||
|
|
*/
|
|||
|
|
export function loadSection(section, data) {
|
|||
|
|
// Load checkboxes
|
|||
|
|
document.querySelectorAll(`input[data-section="${section}"]`).forEach(checkbox => {
|
|||
|
|
if (checkbox.type === 'checkbox') {
|
|||
|
|
const issue = checkbox.dataset.issue
|
|||
|
|
checkbox.checked = data.issues.includes(issue)
|
|||
|
|
updateCheckboxStyle(checkbox)
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// Load comment
|
|||
|
|
const textarea = document.querySelector(`textarea[data-section="${section}"]:not([data-doc-index])`)
|
|||
|
|
if (textarea) {
|
|||
|
|
setInputValue(textarea, data.comment || '')
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Загрузить аннотации документов
|
|||
|
|
* @param {string} section - Секция (docs_from_vectorstore, docs_to_llm)
|
|||
|
|
* @param {string} subsection - Подсекция (research, analytical_hub)
|
|||
|
|
* @param {object} docs - Объект с аннотациями документов
|
|||
|
|
*/
|
|||
|
|
export function loadDocuments(section, subsection, docs) {
|
|||
|
|
if (!docs) return
|
|||
|
|
|
|||
|
|
Object.keys(docs).forEach(docIndex => {
|
|||
|
|
const data = docs[docIndex]
|
|||
|
|
|
|||
|
|
// Load checkboxes
|
|||
|
|
document.querySelectorAll(
|
|||
|
|
`input[data-section="${section}"][data-subsection="${subsection}"][data-doc-index="${docIndex}"]`
|
|||
|
|
).forEach(checkbox => {
|
|||
|
|
if (checkbox.type === 'checkbox') {
|
|||
|
|
const issue = checkbox.dataset.issue
|
|||
|
|
checkbox.checked = data.issues?.includes(issue) || false
|
|||
|
|
updateCheckboxStyle(checkbox)
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// Load comment
|
|||
|
|
const textarea = document.querySelector(
|
|||
|
|
`textarea[data-section="${section}"][data-subsection="${subsection}"][data-doc-index="${docIndex}"]`
|
|||
|
|
)
|
|||
|
|
if (textarea) {
|
|||
|
|
setInputValue(textarea, data.comment || '')
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Настроить обработчики событий для аннотаций
|
|||
|
|
*/
|
|||
|
|
export function setupListeners() {
|
|||
|
|
const env = appState.getCurrentEnv()
|
|||
|
|
const index = env.currentAnswerIndex
|
|||
|
|
|
|||
|
|
// Overall rating
|
|||
|
|
const ratingSelect = document.getElementById('overall-rating')
|
|||
|
|
const overallComment = document.getElementById('overall-comment')
|
|||
|
|
|
|||
|
|
if (ratingSelect) {
|
|||
|
|
ratingSelect.onchange = (e) => {
|
|||
|
|
env.annotations[index].overall.rating = e.target.value
|
|||
|
|
saveDraft()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (overallComment) {
|
|||
|
|
overallComment.oninput = (e) => {
|
|||
|
|
env.annotations[index].overall.comment = getInputValue(e.target)
|
|||
|
|
saveDraft()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Section checkboxes and textareas
|
|||
|
|
document.querySelectorAll('input.checkbox, textarea').forEach(element => {
|
|||
|
|
const section = element.dataset.section
|
|||
|
|
const subsection = element.dataset.subsection
|
|||
|
|
const docIndex = element.dataset.docIndex
|
|||
|
|
|
|||
|
|
if (!section) return
|
|||
|
|
|
|||
|
|
if (element.type === 'checkbox') {
|
|||
|
|
element.onchange = (e) => {
|
|||
|
|
const issue = e.target.dataset.issue
|
|||
|
|
|
|||
|
|
if (docIndex !== undefined) {
|
|||
|
|
// Document annotation
|
|||
|
|
if (!env.annotations[index][section]) {
|
|||
|
|
env.annotations[index][section] = { research: {}, analytical_hub: {} }
|
|||
|
|
}
|
|||
|
|
if (!env.annotations[index][section][subsection]) {
|
|||
|
|
env.annotations[index][section][subsection] = {}
|
|||
|
|
}
|
|||
|
|
if (!env.annotations[index][section][subsection][docIndex]) {
|
|||
|
|
env.annotations[index][section][subsection][docIndex] = { issues: [], comment: '' }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const issues = env.annotations[index][section][subsection][docIndex].issues
|
|||
|
|
|
|||
|
|
if (e.target.checked) {
|
|||
|
|
if (!issues.includes(issue)) issues.push(issue)
|
|||
|
|
} else {
|
|||
|
|
const idx = issues.indexOf(issue)
|
|||
|
|
if (idx > -1) issues.splice(idx, 1)
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// Body annotation
|
|||
|
|
const issues = env.annotations[index][section].issues
|
|||
|
|
|
|||
|
|
if (e.target.checked) {
|
|||
|
|
if (!issues.includes(issue)) issues.push(issue)
|
|||
|
|
} else {
|
|||
|
|
const idx = issues.indexOf(issue)
|
|||
|
|
if (idx > -1) issues.splice(idx, 1)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
updateCheckboxStyle(e.target)
|
|||
|
|
saveDraft()
|
|||
|
|
}
|
|||
|
|
} else if (element.tagName === 'TEXTAREA') {
|
|||
|
|
element.oninput = (e) => {
|
|||
|
|
const value = getInputValue(e.target)
|
|||
|
|
|
|||
|
|
if (docIndex !== undefined) {
|
|||
|
|
// Document annotation
|
|||
|
|
if (!env.annotations[index][section][subsection][docIndex]) {
|
|||
|
|
env.annotations[index][section][subsection][docIndex] = { issues: [], comment: '' }
|
|||
|
|
}
|
|||
|
|
env.annotations[index][section][subsection][docIndex].comment = value
|
|||
|
|
} else if (section !== 'overall') {
|
|||
|
|
// Body annotation
|
|||
|
|
env.annotations[index][section].comment = value
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
saveDraft()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Обновить стиль чекбокса (добавить checked класс к label)
|
|||
|
|
* @param {HTMLElement} checkbox - Чекбокс элемент
|
|||
|
|
*/
|
|||
|
|
export function updateCheckboxStyle(checkbox) {
|
|||
|
|
const label = checkbox.closest('.issue-checkbox')
|
|||
|
|
if (label) {
|
|||
|
|
if (checkbox.checked) {
|
|||
|
|
label.classList.add('checked')
|
|||
|
|
} else {
|
|||
|
|
label.classList.remove('checked')
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Сохранить черновик аннотаций в localStorage
|
|||
|
|
*/
|
|||
|
|
export function saveDraft() {
|
|||
|
|
const currentEnv = appState.getCurrentEnvironment()
|
|||
|
|
appState.saveEnvironmentToStorage(currentEnv)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Export as default object
|
|||
|
|
export default {
|
|||
|
|
initForAnswer,
|
|||
|
|
loadForAnswer,
|
|||
|
|
loadSection,
|
|||
|
|
loadDocuments,
|
|||
|
|
setupListeners,
|
|||
|
|
updateCheckboxStyle,
|
|||
|
|
saveDraft
|
|||
|
|
}
|