push iniziale
This commit is contained in:
335
renderer.js
Normal file
335
renderer.js
Normal file
@@ -0,0 +1,335 @@
|
||||
// Elementi DOM
|
||||
const inputText = document.getElementById('inputText');
|
||||
const outputText = document.getElementById('outputText');
|
||||
const translateBtn = document.getElementById('translateBtn');
|
||||
const detectedLang = document.getElementById('detectedLang');
|
||||
const clearBtn = document.getElementById('clearBtn');
|
||||
const copyBtn = document.getElementById('copyBtn');
|
||||
const charCount = document.getElementById('charCount');
|
||||
const statusIndicator = document.getElementById('statusIndicator');
|
||||
const settingsBtn = document.getElementById('settingsBtn');
|
||||
const settingsModal = document.getElementById('settingsModal');
|
||||
const closeModal = document.getElementById('closeModal');
|
||||
const baseUrlInput = document.getElementById('baseUrl');
|
||||
const modelSelect = document.getElementById('modelSelect');
|
||||
const refreshModels = document.getElementById('refreshModels');
|
||||
const saveSettings = document.getElementById('saveSettings');
|
||||
const toast = document.getElementById('toast');
|
||||
|
||||
// Stato applicazione
|
||||
let isTranslating = false;
|
||||
let currentDetectedLang = null;
|
||||
|
||||
// Carica impostazioni salvate
|
||||
function loadSettings() {
|
||||
const savedBaseUrl = localStorage.getItem('ollamaBaseUrl') || 'http://localhost:11434';
|
||||
const savedModel = localStorage.getItem('ollamaModel') || '';
|
||||
|
||||
baseUrlInput.value = savedBaseUrl;
|
||||
|
||||
if (savedModel) {
|
||||
const existingOption = Array.from(modelSelect.options).find(opt => opt.value === savedModel);
|
||||
if (!existingOption) {
|
||||
const option = document.createElement('option');
|
||||
option.value = savedModel;
|
||||
option.textContent = savedModel;
|
||||
modelSelect.appendChild(option);
|
||||
}
|
||||
modelSelect.value = savedModel;
|
||||
}
|
||||
}
|
||||
|
||||
// Salva impostazioni
|
||||
function saveSettingsData() {
|
||||
const baseUrl = baseUrlInput.value.trim() || 'http://localhost:11434';
|
||||
const model = modelSelect.value;
|
||||
|
||||
localStorage.setItem('ollamaBaseUrl', baseUrl);
|
||||
localStorage.setItem('ollamaModel', model);
|
||||
|
||||
closeSettingsModal();
|
||||
showToast('Impostazioni salvate!');
|
||||
}
|
||||
|
||||
// Carica modelli disponibili
|
||||
async function loadAvailableModels() {
|
||||
const baseUrl = baseUrlInput.value.trim() || 'http://localhost:11434';
|
||||
|
||||
console.log('DEBUG - Tentativo di caricare modelli da:', baseUrl);
|
||||
console.log('DEBUG - electronAPI disponibile?', !!window.electronAPI);
|
||||
console.log('DEBUG - getModels funzione?', typeof window.electronAPI?.getModels);
|
||||
|
||||
refreshModels.disabled = true;
|
||||
refreshModels.innerHTML = `
|
||||
<svg class="spin" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="23 4 23 10 17 10"/>
|
||||
<polyline points="1 20 1 14 7 14"/>
|
||||
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
|
||||
</svg>
|
||||
Caricamento...
|
||||
`;
|
||||
|
||||
try {
|
||||
const cleanBaseUrl = baseUrl.trim();
|
||||
console.log('Tentativo connessione a:', cleanBaseUrl);
|
||||
const result = await window.electronAPI.getModels(cleanBaseUrl);
|
||||
|
||||
if (result.success) {
|
||||
modelSelect.innerHTML = '<option value="">Seleziona un modello...</option>';
|
||||
|
||||
result.models.forEach(model => {
|
||||
const option = document.createElement('option');
|
||||
option.value = model.name;
|
||||
option.textContent = model.name;
|
||||
modelSelect.appendChild(option);
|
||||
});
|
||||
|
||||
const savedModel = localStorage.getItem('ollamaModel');
|
||||
if (savedModel && result.models.find(m => m.name === savedModel)) {
|
||||
modelSelect.value = savedModel;
|
||||
}
|
||||
|
||||
showToast(`${result.models.length} modelli trovati!`);
|
||||
} else {
|
||||
showToast('Errore: ' + result.error, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Errore completo:', error);
|
||||
showToast('Errore di connessione: ' + (error.message || 'Verifica che Ollama sia in esecuzione'), 'error');
|
||||
} finally {
|
||||
refreshModels.disabled = false;
|
||||
refreshModels.innerHTML = `
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="23 4 23 10 17 10"/>
|
||||
<polyline points="1 20 1 14 7 14"/>
|
||||
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
|
||||
</svg>
|
||||
Aggiorna
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// Rileva la lingua del testo
|
||||
function detectLanguage(text) {
|
||||
if (!text.trim()) {
|
||||
currentDetectedLang = null;
|
||||
detectedLang.textContent = 'Rilevamento automatico...';
|
||||
detectedLang.className = '';
|
||||
return null;
|
||||
}
|
||||
|
||||
// Caratteri italiani comuni
|
||||
const italianChars = /[àèéìòùÀÈÉÌÒÙ]/;
|
||||
const italianWords = /\b(ciao|grazie|buongiorno|come|sono|per|con|su|tra|fra|nel|del|degli|della)\b/gi;
|
||||
|
||||
// Caratteri inglesi comuni (rari in italiano)
|
||||
const englishChars = /[wkyjWQX]/;
|
||||
const englishWords = /\b(the|and|for|are|with|you|that|have|this|from|they|we|say|her|she|or|an|will|my|one|all|would|there|their|what|so|up|out|if|about|who|get|which|go|me|when|make|can|like|time|no|just|him|know|take|people|into|year|your|good|some|could|them|see|other|than|then|now|look|only|come|its|over|think|also|back|after|use|two|how|our|work|first|well|way|even|new|want|because|any|these|give|day|most|us)\b/gi;
|
||||
|
||||
const hasItalianChars = italianChars.test(text);
|
||||
const hasItalianWords = (text.match(italianWords) || []).length;
|
||||
const hasEnglishChars = englishChars.test(text);
|
||||
const hasEnglishWords = (text.match(englishWords) || []).length;
|
||||
|
||||
// Logica di rilevamento
|
||||
let detected = null;
|
||||
|
||||
if (hasItalianChars) {
|
||||
detected = 'it';
|
||||
} else if (hasEnglishChars && !hasItalianWords) {
|
||||
detected = 'en';
|
||||
} else if (hasItalianWords > hasEnglishWords) {
|
||||
detected = 'it';
|
||||
} else if (hasEnglishWords > italianWords) {
|
||||
detected = 'en';
|
||||
} else {
|
||||
// Fallback: verifica la distribuzione delle vocali
|
||||
const vowels = text.toLowerCase().match(/[aeiou]/g) || [];
|
||||
const yCount = (text.match(/y/gi) || []).length;
|
||||
if (yCount > 0) {
|
||||
detected = 'en';
|
||||
} else {
|
||||
detected = 'it'; // default
|
||||
}
|
||||
}
|
||||
|
||||
currentDetectedLang = detected;
|
||||
updateLanguageIndicator();
|
||||
return detected;
|
||||
}
|
||||
|
||||
// Aggiorna l'indicatore di lingua
|
||||
function updateLanguageIndicator() {
|
||||
if (currentDetectedLang === 'it') {
|
||||
detectedLang.innerHTML = '🇮🇹 Italiano → 🇬🇧 Inglese';
|
||||
detectedLang.className = 'detected-it';
|
||||
} else if (currentDetectedLang === 'en') {
|
||||
detectedLang.innerHTML = '🇬🇧 Inglese → 🇮🇹 Italiano';
|
||||
detectedLang.className = 'detected-en';
|
||||
} else {
|
||||
detectedLang.textContent = 'Rilevamento automatico...';
|
||||
detectedLang.className = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Traduci testo
|
||||
async function translateText() {
|
||||
const text = inputText.value.trim();
|
||||
|
||||
if (!text) {
|
||||
showToast('Inserisci del testo da tradurre', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const baseUrl = localStorage.getItem('ollamaBaseUrl') || 'http://localhost:11434';
|
||||
const model = localStorage.getItem('ollamaModel');
|
||||
|
||||
if (!model) {
|
||||
showToast('Configura un modello nelle impostazioni', 'warning');
|
||||
openSettingsModal();
|
||||
return;
|
||||
}
|
||||
|
||||
// Rileva la lingua se non già rilevata
|
||||
const detected = currentDetectedLang || detectLanguage(text);
|
||||
if (!detected) {
|
||||
showToast('Impossibile rilevare la lingua', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const direction = detected === 'en' ? 'en-it' : 'it-en';
|
||||
|
||||
isTranslating = true;
|
||||
translateBtn.disabled = true;
|
||||
translateBtn.innerHTML = `
|
||||
<span class="spinner"></span>
|
||||
<span class="btn-text">Traduzione...</span>
|
||||
`;
|
||||
statusIndicator.className = 'status-indicator translating';
|
||||
|
||||
try {
|
||||
const result = await window.electronAPI.translateText({
|
||||
text,
|
||||
direction,
|
||||
model,
|
||||
baseUrl
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
outputText.value = result.translation.trim();
|
||||
statusIndicator.className = 'status-indicator success';
|
||||
showToast('Traduzione completata!', 'success');
|
||||
} else {
|
||||
outputText.value = '';
|
||||
statusIndicator.className = 'status-indicator error';
|
||||
showToast('Errore: ' + result.error, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
outputText.value = '';
|
||||
statusIndicator.className = 'status-indicator error';
|
||||
showToast('Errore: ' + error.message, 'error');
|
||||
} finally {
|
||||
isTranslating = false;
|
||||
translateBtn.disabled = false;
|
||||
translateBtn.innerHTML = `
|
||||
<span class="btn-text">Traduci</span>
|
||||
<svg class="btn-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M5 12h14"/>
|
||||
<path d="M12 5l7 7-7 7"/>
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// Aggiorna conteggio caratteri
|
||||
function updateCharCount() {
|
||||
const count = inputText.value.length;
|
||||
charCount.textContent = `${count} caratter${count === 1 ? 'e' : 'i'}`;
|
||||
}
|
||||
|
||||
// Copia negli appunti
|
||||
async function copyToClipboard() {
|
||||
const text = outputText.value;
|
||||
if (!text) return;
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
showToast('Testo copiato!', 'success');
|
||||
} catch (err) {
|
||||
outputText.select();
|
||||
document.execCommand('copy');
|
||||
showToast('Testo copiato!', 'success');
|
||||
}
|
||||
}
|
||||
|
||||
// Pulisci input
|
||||
function clearInput() {
|
||||
inputText.value = '';
|
||||
outputText.value = '';
|
||||
updateCharCount();
|
||||
statusIndicator.className = 'status-indicator';
|
||||
currentDetectedLang = null;
|
||||
detectedLang.textContent = 'Rilevamento automatico...';
|
||||
detectedLang.className = '';
|
||||
inputText.focus();
|
||||
}
|
||||
|
||||
// Mostra toast
|
||||
function showToast(message, type = 'info') {
|
||||
toast.textContent = message;
|
||||
toast.className = `toast show ${type}`;
|
||||
|
||||
setTimeout(() => {
|
||||
toast.classList.remove('show');
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Gestione modale
|
||||
function openSettingsModal() {
|
||||
settingsModal.classList.add('show');
|
||||
loadAvailableModels();
|
||||
}
|
||||
|
||||
function closeSettingsModal() {
|
||||
settingsModal.classList.remove('show');
|
||||
}
|
||||
|
||||
// Event Listeners
|
||||
translateBtn.addEventListener('click', translateText);
|
||||
inputText.addEventListener('input', () => {
|
||||
updateCharCount();
|
||||
detectLanguage(inputText.value);
|
||||
});
|
||||
clearBtn.addEventListener('click', clearInput);
|
||||
copyBtn.addEventListener('click', copyToClipboard);
|
||||
settingsBtn.addEventListener('click', openSettingsModal);
|
||||
closeModal.addEventListener('click', closeSettingsModal);
|
||||
refreshModels.addEventListener('click', loadAvailableModels);
|
||||
saveSettings.addEventListener('click', saveSettingsData);
|
||||
|
||||
// Chiudi modale con ESC
|
||||
settingsModal.addEventListener('click', (e) => {
|
||||
if (e.target === settingsModal) {
|
||||
closeSettingsModal();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
closeSettingsModal();
|
||||
}
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
|
||||
translateText();
|
||||
}
|
||||
});
|
||||
|
||||
// Ascolta richiesta apertura impostazioni dal menu
|
||||
window.electronAPI.onOpenSettings((event) => {
|
||||
openSettingsModal();
|
||||
});
|
||||
|
||||
// Inizializzazione
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
loadSettings();
|
||||
updateCharCount();
|
||||
});
|
||||
Reference in New Issue
Block a user