feat(ai): integrate local Ollama AI for in-context sentence translation and caching (Task 13)
This commit is contained in:
@@ -1151,6 +1151,27 @@ export async function openWordPopup(term, contextSentence, targetElement) {
|
||||
if (popupLoading) popupLoading.classList.remove('hidden');
|
||||
if (popupContent) popupContent.classList.add('hidden');
|
||||
|
||||
const popupTranslateSentenceBtn = document.getElementById('popup-translate-sentence-btn');
|
||||
const popupAiSourceBadge = document.getElementById('popup-ai-source-badge');
|
||||
const popupSentenceLoading = document.getElementById('popup-sentence-loading');
|
||||
const popupSentenceTranslationWrap = document.getElementById('popup-sentence-translation-wrap');
|
||||
const popupSentenceRu = document.getElementById('popup-sentence-ru');
|
||||
const popupContextMeaningWrap = document.getElementById('popup-context-meaning-wrap');
|
||||
const popupContextMeaningVal = document.getElementById('popup-context-meaning-val');
|
||||
const popupSentenceAiBar = document.querySelector('.popup-sentence-ai-bar');
|
||||
|
||||
if (popupSentenceLoading) popupSentenceLoading.classList.add('hidden');
|
||||
if (popupSentenceTranslationWrap) popupSentenceTranslationWrap.classList.add('hidden');
|
||||
if (popupSentenceRu) popupSentenceRu.textContent = '';
|
||||
if (popupContextMeaningWrap) popupContextMeaningWrap.classList.add('hidden');
|
||||
if (popupContextMeaningVal) popupContextMeaningVal.textContent = '';
|
||||
if (popupAiSourceBadge) popupAiSourceBadge.classList.add('hidden');
|
||||
if (popupTranslateSentenceBtn) {
|
||||
popupTranslateSentenceBtn.disabled = false;
|
||||
popupTranslateSentenceBtn.textContent = '🌐 Перевести предложение';
|
||||
if (popupSentenceAiBar) popupSentenceAiBar.classList.toggle('hidden', !contextSentence);
|
||||
}
|
||||
|
||||
if (popupInLibrary) {
|
||||
popupInLibrary.classList.toggle('hidden', !existingCard);
|
||||
}
|
||||
@@ -1831,6 +1852,78 @@ export function initReader() {
|
||||
window.addEventListener('scroll', schedulePopupUpdate, { passive: true, capture: true });
|
||||
window.addEventListener('resize', schedulePopupUpdate, { passive: true });
|
||||
|
||||
const popupTranslateSentenceBtn = document.getElementById('popup-translate-sentence-btn');
|
||||
const popupUseContextMeaningBtn = document.getElementById('popup-use-context-meaning-btn');
|
||||
const popupSentenceLoading = document.getElementById('popup-sentence-loading');
|
||||
const popupSentenceTranslationWrap = document.getElementById('popup-sentence-translation-wrap');
|
||||
const popupSentenceRu = document.getElementById('popup-sentence-ru');
|
||||
const popupContextMeaningWrap = document.getElementById('popup-context-meaning-wrap');
|
||||
const popupContextMeaningVal = document.getElementById('popup-context-meaning-val');
|
||||
const popupAiSourceBadge = document.getElementById('popup-ai-source-badge');
|
||||
|
||||
if (popupTranslateSentenceBtn) {
|
||||
popupTranslateSentenceBtn.addEventListener('click', async (e) => {
|
||||
e.stopPropagation();
|
||||
if (!popupCurrentData || !popupCurrentData.contextSentence) return;
|
||||
const ctxSentence = popupCurrentData.contextSentence;
|
||||
const term = popupCurrentData.term;
|
||||
|
||||
popupTranslateSentenceBtn.disabled = true;
|
||||
if (popupSentenceLoading) popupSentenceLoading.classList.remove('hidden');
|
||||
if (popupSentenceTranslationWrap) popupSentenceTranslationWrap.classList.add('hidden');
|
||||
if (popupContextMeaningWrap) popupContextMeaningWrap.classList.add('hidden');
|
||||
|
||||
try {
|
||||
const res = await api.lookup.translateSentence(ctxSentence, term);
|
||||
if (popupSentenceLoading) popupSentenceLoading.classList.add('hidden');
|
||||
if (popupSentenceTranslationWrap) popupSentenceTranslationWrap.classList.remove('hidden');
|
||||
|
||||
if (popupSentenceRu) {
|
||||
popupSentenceRu.textContent = res.sentence_ru || 'Перевод не получен';
|
||||
}
|
||||
|
||||
if (res.word_meaning && res.word_meaning.trim()) {
|
||||
popupCurrentData.contextMeaning = res.word_meaning.trim();
|
||||
if (popupContextMeaningWrap) popupContextMeaningWrap.classList.remove('hidden');
|
||||
if (popupContextMeaningVal) popupContextMeaningVal.textContent = res.word_meaning.trim();
|
||||
} else {
|
||||
if (popupContextMeaningWrap) popupContextMeaningWrap.classList.add('hidden');
|
||||
}
|
||||
|
||||
if (popupAiSourceBadge) {
|
||||
popupAiSourceBadge.classList.remove('hidden');
|
||||
popupAiSourceBadge.textContent = res.cached ? '⚡ Кеш (0.01ms)' : '⚡ Local AI (Qwen)';
|
||||
popupAiSourceBadge.title = res.source || '';
|
||||
}
|
||||
|
||||
positionWordPopup(popupCurrentData.targetElement);
|
||||
} catch (err) {
|
||||
console.error('Sentence translation error:', err);
|
||||
if (popupSentenceLoading) popupSentenceLoading.classList.add('hidden');
|
||||
if (popupSentenceTranslationWrap) popupSentenceTranslationWrap.classList.remove('hidden');
|
||||
if (popupSentenceRu) popupSentenceRu.textContent = 'Ошибка связи с локальным ИИ';
|
||||
} finally {
|
||||
popupTranslateSentenceBtn.disabled = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (popupUseContextMeaningBtn) {
|
||||
popupUseContextMeaningBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
if (!popupCurrentData || !popupCurrentData.contextMeaning) return;
|
||||
const meaning = popupCurrentData.contextMeaning;
|
||||
if (popupTranslation) popupTranslation.textContent = meaning;
|
||||
if (popupCurrentData.lookup) {
|
||||
popupCurrentData.lookup.translation = meaning;
|
||||
}
|
||||
popupUseContextMeaningBtn.textContent = '✓ Вставлено!';
|
||||
setTimeout(() => {
|
||||
if (popupUseContextMeaningBtn) popupUseContextMeaningBtn.textContent = 'Вставить в карточку';
|
||||
}, 1800);
|
||||
});
|
||||
}
|
||||
|
||||
if (popupSaveBtn) {
|
||||
popupSaveBtn.addEventListener('click', async () => {
|
||||
if (!popupCurrentData) return;
|
||||
|
||||
Reference in New Issue
Block a user