feat: implement Web Worker architecture for efficient data sync
- Add data-sync.worker.js: separate thread for API calls (30s interval) - Add app.js: main thread with DOM update logic and localStorage integration - Update index.html: remove inline scripts, use external app.js - Implement granular DOM updates (only update changed elements) - Add localStorage persistence for health and models data - Add Web Worker fallback for unsupported browsers - Add WEB_WORKERS.md documentation with architecture details Benefits: - Main thread never blocked by network requests - UI stays responsive at 60 FPS - Offline support via localStorage - Efficient DOM updates (no unnecessary re-renders) - Better browser support and performance
This commit is contained in:
@@ -59,7 +59,7 @@
|
||||
<div class="bg-gray-800 rounded-lg border border-gray-700 p-6">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h2 class="text-xl font-bold">Modelli Disponibili</h2>
|
||||
<button onclick="loadModels()" class="bg-purple-600 hover:bg-purple-700 px-4 py-2 rounded-lg text-sm font-medium transition">
|
||||
<button id="refresh-btn" class="bg-purple-600 hover:bg-purple-700 px-4 py-2 rounded-lg text-sm font-medium transition">
|
||||
🔄 Aggiorna
|
||||
</button>
|
||||
</div>
|
||||
@@ -97,128 +97,6 @@
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const API_BASE = "/api/v1";
|
||||
|
||||
// Formattare bytes in formato leggibile
|
||||
function formatBytes(bytes) {
|
||||
if (bytes === 0) return "0 B";
|
||||
const k = 1024;
|
||||
const sizes = ["B", "KB", "MB", "GB"];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return (bytes / Math.pow(k, i)).toFixed(2) + " " + sizes[i];
|
||||
}
|
||||
|
||||
// Formattare data
|
||||
function formatDate(dateString) {
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleDateString("it-IT", {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit"
|
||||
});
|
||||
}
|
||||
|
||||
// Verificare health
|
||||
async function checkHealth() {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/health`);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const statusEl = document.getElementById("status-indicator");
|
||||
const statusText = document.getElementById("status-text");
|
||||
const ollamaStatus = data.ollama_status;
|
||||
|
||||
if (ollamaStatus === "online") {
|
||||
statusEl.className = "w-3 h-3 bg-green-500 rounded-full";
|
||||
statusText.className = "text-sm text-green-400";
|
||||
statusText.textContent = "Ollama Online";
|
||||
document.getElementById("ollama-status").innerHTML = "🟢 Online";
|
||||
} else {
|
||||
statusEl.className = "w-3 h-3 bg-red-500 rounded-full";
|
||||
statusText.className = "text-sm text-red-400";
|
||||
statusText.textContent = "Ollama Offline";
|
||||
document.getElementById("ollama-status").innerHTML = "🔴 Offline";
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Health check error:", error);
|
||||
document.getElementById("status-indicator").className = "w-3 h-3 bg-red-500 rounded-full";
|
||||
document.getElementById("status-text").textContent = "Errore connessione";
|
||||
}
|
||||
}
|
||||
|
||||
// Caricare modelli
|
||||
async function loadModels() {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/models`);
|
||||
if (!response.ok) throw new Error("Errore nel caricamento");
|
||||
|
||||
const data = await response.json();
|
||||
const models = data.models || [];
|
||||
|
||||
// Aggiornare conteggio
|
||||
document.getElementById("models-count").textContent = models.length;
|
||||
|
||||
// Calcolare spazio totale
|
||||
const totalSize = models.reduce((sum, m) => sum + m.size, 0);
|
||||
document.getElementById("total-size").textContent = formatBytes(totalSize);
|
||||
|
||||
// Renderizzare modelli
|
||||
if (models.length === 0) {
|
||||
document.getElementById("models-container").innerHTML = `
|
||||
<div class="text-center py-8 text-gray-400">
|
||||
<p>Nessun modello caricato</p>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
document.getElementById("models-container").innerHTML = models.map(model => `
|
||||
<div class="bg-gray-700 rounded-lg p-4 border border-gray-600 hover:border-purple-500 transition">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<h3 class="text-lg font-semibold">${model.name}</h3>
|
||||
<span class="bg-purple-600 px-3 py-1 rounded text-xs font-medium">Caricato</span>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4 text-sm">
|
||||
<div>
|
||||
<p class="text-gray-400">Dimensione</p>
|
||||
<p class="font-semibold">${formatBytes(model.size)}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-gray-400">Ultimo aggiornamento</p>
|
||||
<p class="font-semibold">${formatDate(model.modified_at)}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<p class="text-gray-400 text-xs">Digest</p>
|
||||
<p class="font-mono text-xs bg-gray-800 p-2 rounded mt-1 break-all">${model.digest.substring(0, 64)}...</p>
|
||||
</div>
|
||||
</div>
|
||||
`).join("");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading models:", error);
|
||||
document.getElementById("models-container").innerHTML = `
|
||||
<div class="text-center py-8 text-red-400">
|
||||
<p>❌ Errore nel caricamento dei modelli</p>
|
||||
<p class="text-sm mt-2">${error.message}</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// Inizializzazione
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
checkHealth();
|
||||
loadModels();
|
||||
|
||||
// Refresh ogni 30 secondi
|
||||
setInterval(() => {
|
||||
checkHealth();
|
||||
loadModels();
|
||||
}, 30000);
|
||||
});
|
||||
</script>
|
||||
<script src="/static/js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user