MyContexto / index.html
unique2101's picture
Update index.html
b5fb964 verified
Raw
History Blame Contribute Delete
5.32 kB
<!DOCTYPE html>
<html lang="cs">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>🔥 České Contexto</title>
<style>
body { font-family: system-ui, -apple-system, sans-serif; max-width: 500px; margin: 30px auto; padding: 20px; background: #f8f9fa; color: #212529; }
.card { background: white; padding: 24px; border-radius: 16px; box-shadow: 0 4px 12px rgba(0,0,0,0.08); margin-bottom: 20px; }
h1 { text-align: center; color: #d97706; margin-top: 0; }
input, button { width: 100%; padding: 12px; margin-top: 8px; border-radius: 8px; border: 1px solid #ced4da; box-sizing: border-box; font-size: 16px; }
button { background: #d97706; color: white; border: none; font-weight: bold; cursor: pointer; transition: background 0.2s; }
button:hover { background: #b45309; }
button:disabled { background: #adb5bd; cursor: not-allowed; }
.result-item { margin: 8px 0; padding: 12px; border-radius: 8px; display: flex; justify-content: space-between; font-weight: bold; }
.hot { background: #fee2e2; color: #dc2626; }
.warm { background: #fef3c7; color: #d97706; }
.cold { background: #e0f2fe; color: #0284c7; }
#status { text-align: center; font-size: 0.9em; color: #6c757d; margin-bottom: 15px; }
details { margin-bottom: 15px; background: #f1f3f5; padding: 10px; border-radius: 8px; }
</style>
</head>
<body>
<div class="card">
<h1>🔥 České Contexto</h1>
<div id="status">⏳ Načítám AI model (stahuje se jen při prvním načtení)...</div>
<details>
<summary>⚙️ Nastavení tajného slova (pro správce)</summary>
<input type="password" id="secretWordInput" value="pivo" placeholder="Zadej tajné slovo">
</details>
<form id="guessForm">
<input type="text" id="guessInput" placeholder="Zadej české slovo..." disabled required autocomplete="off">
<button type="submit" id="submitBtn" disabled>Hádat</button>
</form>
</div>
<div class="card">
<h3 style="margin-top:0;">Tvoje pokusy:</h3>
<div id="results"></div>
</div>
<script type="module">
import { pipeline, cos_sim } from 'https://cdn.jsdelivr.net/npm/@xenova/transformers@2.14.0';
let extractor = null;
let secretEmbedding = null;
const guesses = [];
const statusEl = document.getElementById('status');
const guessInput = document.getElementById('guessInput');
const submitBtn = document.getElementById('submitBtn');
const secretInput = document.getElementById('secretWordInput');
const resultsEl = document.getElementById('results');
// Vyvážená kalibrace teploty (každá setina podobnosti nad 0.72 přidá ~3 °C)
function calculateTemperature(sim) {
const minSim = 0.72; // Hranice pro zcela nesouvisející slova
if (sim <= minSim) {
// Zcela mimo slova zůstanou v modrém pásmu (0 až 15 °C)
return Math.max(0, Math.round((sim / minSim) * 15 * 10) / 10);
}
// Plynulé lineární roztažení od 15 °C do 100 °C
const norm = (sim - minSim) / (1.0 - minSim);
const score = 15 + (norm * 85);
return Math.min(100, Math.round(score * 10) / 10);
}
async function init() {
try {
extractor = await pipeline('feature-extraction', 'Xenova/multilingual-e5-small');
statusEl.innerText = "✅ Kalibrovaný AI model načten! Můžeš hádat.";
guessInput.disabled = false;
submitBtn.disabled = false;
await updateSecret();
} catch (e) {
statusEl.innerText = "❌ Chyba při načítání modelu: " + e.message;
}
}
async function getEmbedding(text) {
// Symetrický prefix "query: " výrazně zpřesňuje porovnávání jednotlivých slov
const output = await extractor('query: ' + text.trim().toLowerCase(), { pooling: 'mean', normalize: true });
return output.data;
}
async function updateSecret() {
if (!extractor) return;
const word = secretInput.value.trim().toLowerCase();
if (word) {
secretEmbedding = await getEmbedding(word);
}
}
secretInput.addEventListener('change', updateSecret);
document.getElementById('guessForm').addEventListener('submit', async (e) => {
e.preventDefault();
const word = guessInput.value.trim().toLowerCase();
if (!word || !secretEmbedding) return;
guessInput.value = '';
const guessEmb = await getEmbedding(word);
const rawSim = cos_sim(secretEmbedding, guessEmb);
// Aplikujeme kalibrační vzorec
const score = calculateTemperature(rawSim);
guesses.push({ word, score });
guesses.sort((a, b) => b.score - a.score);
renderResults();
});
function renderResults() {
resultsEl.innerHTML = '';
guesses.forEach(g => {
const div = document.createElement('div');
let cls = 'cold';
let icon = '🔵';
if (g.score >= 75) { cls = 'hot'; icon = '🔴'; }
else if (g.score >= 45) { cls = 'warm'; icon = '🟠'; }
div.className = `result-item ${cls}`;
div.innerHTML = `<span>${icon} ${g.word}</span> <span>${g.score} °C</span>`;
resultsEl.appendChild(div);
});
}
init();
</script>