Spaces:
Sleeping
Sleeping
Update templates/index.html
Browse files- templates/index.html +67 -4
templates/index.html
CHANGED
|
@@ -156,6 +156,19 @@ h1.ht .l3 { display: block; color: var(--muted2); }
|
|
| 156 |
}
|
| 157 |
.src-warn { font-family: var(--mono); font-size: 0.55rem; color: #f59e0b; margin-top: 0.4rem; display: none; }
|
| 158 |
.src-warn.show { display: block; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
|
| 160 |
.btn-go {
|
| 161 |
align-self: flex-end;
|
|
@@ -429,7 +442,17 @@ footer {
|
|
| 429 |
</div>
|
| 430 |
<p class="src-warn" id="src-warn">⚠ Pilih minimal satu sumber data</p>
|
| 431 |
</div>
|
| 432 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 433 |
</div>
|
| 434 |
|
| 435 |
<div class="stats">
|
|
@@ -565,9 +588,49 @@ document.addEventListener('change', () => {
|
|
| 565 |
document.getElementById('src-warn').classList.remove('show');
|
| 566 |
});
|
| 567 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 568 |
async function go() {
|
| 569 |
-
const
|
| 570 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 571 |
|
| 572 |
if (!kw) {
|
| 573 |
const inp = document.getElementById('keyword');
|
|
@@ -590,7 +653,7 @@ async function go() {
|
|
| 590 |
const r = await fetch('/analyze', {
|
| 591 |
method: 'POST',
|
| 592 |
headers: { 'Content-Type': 'application/json' },
|
| 593 |
-
body: JSON.stringify({ keyword: kw, source: src })
|
| 594 |
});
|
| 595 |
const d = await r.json();
|
| 596 |
clearTimeout(st);
|
|
|
|
| 156 |
}
|
| 157 |
.src-warn { font-family: var(--mono); font-size: 0.55rem; color: #f59e0b; margin-top: 0.4rem; display: none; }
|
| 158 |
.src-warn.show { display: block; }
|
| 159 |
+
/* SLIDER */
|
| 160 |
+
.slider-wrap { display:flex; flex-direction:column; gap:0.35rem; margin-top:0.1rem; }
|
| 161 |
+
.slider-row { display:flex; align-items:center; gap:0.75rem; }
|
| 162 |
+
input[type="range"] { flex:1; -webkit-appearance:none; appearance:none; height:3px; background:var(--b2); border-radius:2px; outline:none; cursor:pointer; }
|
| 163 |
+
input[type="range"]::-webkit-slider-thumb { -webkit-appearance:none; width:14px; height:14px; border-radius:50%; background:linear-gradient(135deg,var(--accent),var(--a2)); cursor:pointer; transition:transform .15s; }
|
| 164 |
+
input[type="range"]::-webkit-slider-thumb:hover { transform:scale(1.2); }
|
| 165 |
+
.slider-val { font-family:var(--mono); font-size:0.72rem; font-weight:500; color:var(--accent); min-width:2.5rem; text-align:right; }
|
| 166 |
+
/* MULTI-KEYWORD */
|
| 167 |
+
.kw-tags { display:flex; flex-wrap:wrap; gap:0.35rem; margin-top:0.4rem; min-height:1.5rem; }
|
| 168 |
+
.kw-tag { display:inline-flex; align-items:center; gap:0.3rem; background:rgba(79,156,249,0.1); border:1px solid rgba(79,156,249,0.25); border-radius:5px; padding:0.15rem 0.5rem; font-family:var(--mono); font-size:0.62rem; color:var(--accent); }
|
| 169 |
+
.kw-tag button { background:none; border:none; color:var(--accent); cursor:pointer; font-size:0.7rem; line-height:1; padding:0; opacity:0.7; }
|
| 170 |
+
.kw-tag button:hover { opacity:1; }
|
| 171 |
+
.kw-add-hint { font-family:var(--mono); font-size:0.55rem; color:var(--muted); }
|
| 172 |
|
| 173 |
.btn-go {
|
| 174 |
align-self: flex-end;
|
|
|
|
| 442 |
</div>
|
| 443 |
<p class="src-warn" id="src-warn">⚠ Pilih minimal satu sumber data</p>
|
| 444 |
</div>
|
| 445 |
+
<div class="kw-tags" id="kw-tags"></div>
|
| 446 |
+
<p class="kw-add-hint">Tekan <b>Enter</b> atau <b>,</b> untuk tambah keyword · Maks. 3 keyword</p>
|
| 447 |
+
|
| 448 |
+
<div class="slider-wrap" style="margin-top:0.75rem">
|
| 449 |
+
<label style="font-family:var(--mono);font-size:0.58rem;letter-spacing:0.14em;text-transform:uppercase;color:var(--muted)">Confidence Threshold</label>
|
| 450 |
+
<div class="slider-row">
|
| 451 |
+
<input type="range" id="conf-slider" min="0.3" max="0.9" step="0.05" value="0.6">
|
| 452 |
+
<span class="slider-val" id="conf-val">0.60</span>
|
| 453 |
+
</div>
|
| 454 |
+
</div>
|
| 455 |
+
<p class="f-hint" style="margin-top:0.5rem">Prediksi di bawah threshold akan ditandai <b>Uncertain</b></p>
|
| 456 |
</div>
|
| 457 |
|
| 458 |
<div class="stats">
|
|
|
|
| 588 |
document.getElementById('src-warn').classList.remove('show');
|
| 589 |
});
|
| 590 |
|
| 591 |
+
// ── Multi-keyword management ──
|
| 592 |
+
const kwTags = [];
|
| 593 |
+
document.getElementById('keyword').addEventListener('keydown', function(e) {
|
| 594 |
+
if ((e.key === 'Enter' || e.key === ',') && this.value.trim()) {
|
| 595 |
+
e.preventDefault();
|
| 596 |
+
addKeyword(this.value.trim().replace(',',''));
|
| 597 |
+
this.value = '';
|
| 598 |
+
}
|
| 599 |
+
});
|
| 600 |
+
|
| 601 |
+
function addKeyword(kw) {
|
| 602 |
+
if (!kw || kwTags.includes(kw) || kwTags.length >= 3) return;
|
| 603 |
+
kwTags.push(kw);
|
| 604 |
+
renderTags();
|
| 605 |
+
}
|
| 606 |
+
|
| 607 |
+
function removeKeyword(kw) {
|
| 608 |
+
const idx = kwTags.indexOf(kw);
|
| 609 |
+
if (idx > -1) kwTags.splice(idx, 1);
|
| 610 |
+
renderTags();
|
| 611 |
+
}
|
| 612 |
+
|
| 613 |
+
function renderTags() {
|
| 614 |
+
const container = document.getElementById('kw-tags');
|
| 615 |
+
container.innerHTML = kwTags.map(k =>
|
| 616 |
+
`<span class="kw-tag">${k}<button onclick="removeKeyword('${k}')">×</button></span>`
|
| 617 |
+
).join('');
|
| 618 |
+
}
|
| 619 |
+
|
| 620 |
+
// ── Confidence slider ──
|
| 621 |
+
document.getElementById('conf-slider').addEventListener('input', function() {
|
| 622 |
+
document.getElementById('conf-val').textContent = parseFloat(this.value).toFixed(2);
|
| 623 |
+
});
|
| 624 |
+
|
| 625 |
async function go() {
|
| 626 |
+
const inputKw = document.getElementById('keyword').value.trim();
|
| 627 |
+
if (inputKw) addKeyword(inputKw);
|
| 628 |
+
document.getElementById('keyword').value = '';
|
| 629 |
+
|
| 630 |
+
const allKw = kwTags.length ? kwTags : [inputKw];
|
| 631 |
+
const kw = allKw.filter(Boolean).join(',');
|
| 632 |
+
const src = getSelectedSources();
|
| 633 |
+
const confTh = parseFloat(document.getElementById('conf-slider').value);
|
| 634 |
|
| 635 |
if (!kw) {
|
| 636 |
const inp = document.getElementById('keyword');
|
|
|
|
| 653 |
const r = await fetch('/analyze', {
|
| 654 |
method: 'POST',
|
| 655 |
headers: { 'Content-Type': 'application/json' },
|
| 656 |
+
body: JSON.stringify({ keyword: kw, source: src, conf_threshold: confTh })
|
| 657 |
});
|
| 658 |
const d = await r.json();
|
| 659 |
clearTimeout(st);
|