ks-version-1-1 / templates /results.html
NIKKI77's picture
Deploy: GPU-ready HF Space (Docker)
903b444
<!-- Results page (results.html) — shows search results with semantic toggle, autocomplete, and pagination -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Search Results</title>
<link rel="preconnect" href="https://www.youtube.com">
<link rel="preconnect" href="https://img.youtube.com">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Theme bootstrap -->
<script>
try {
var t = localStorage.getItem('theme');
if (t) { document.documentElement.dataset.theme = t; }
} catch (e) {}
</script>
<style>
:root {
--bg: #ffffff;
--bg-soft: #f9f9f9;
--text: #222;
--text-muted: #555;
--accent: #007bff;
--accent-dark: #0056b3;
--good: #28a745;
--good-dark: #218838;
--warn-bg: #fff3cd;
--warn-text: #856404;
--warn-accent: #ffc107;
--shadow: rgba(0,0,0,0.08);
--shadow-strong: rgba(0,0,0,0.15);
--border: #eee;
}
html[data-theme="dark"] {
--bg: #0e0f12;
--bg-soft: #15171c;
--text: #e7e9ee;
--text-muted: #b3b8c4;
--accent: #4ea1ff;
--accent-dark: #2d7ed8;
--good: #39d353;
--good-dark: #29943a;
--warn-bg: #2a2206;
--warn-text: #f0d08a;
--warn-accent: #ffd15a;
--shadow: rgba(0,0,0,0.4);
--shadow-strong: rgba(0,0,0,0.6);
--border: #333;
}
* { box-sizing: border-box; }
body {
font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
padding: 30px;
background: linear-gradient(to bottom right, #f3f4f6, #ffffff);
color: var(--text);
}
body, .result-card, .context-block { transition: background-color .2s, color .2s, box-shadow .2s; }
html[data-theme="dark"] body { background: #0e0f12; }
h1 { color: var(--text); margin-bottom: 10px; }
/* Search bar */
.search-wrap {
position: relative;
max-width: 600px;
width: 100%;
margin: 0 auto 20px auto;
}
.search-row {
display: flex;
gap: 0;
align-items: stretch;
}
#queryInput {
flex: 1 1 auto;
min-width: 0;
height: 42px;
box-sizing: border-box;
padding: 10px 12px 10px 40px;
font-size: 16px;
border: 1px solid var(--border);
border-radius: 8px 0 0 8px;
background:
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%2399a3ad' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='11' cy='11' r='8'/><line x1='21' y1='21' x2='16.65' y2='16.65'/></svg>")
no-repeat 12px center / 18px 18px #fff;
color: #111;
background-color: #fff;
}
#queryInput::placeholder { color: #6b7280; }
.search-btn {
height: 42px;
padding: 0 16px;
font-size: 15px;
font-weight: 600;
color: #fff;
background: var(--accent);
border: 1px solid var(--accent);
border-radius: 0 8px 8px 0;
cursor: pointer;
flex: 0 0 auto;
}
.search-btn:hover { background: var(--accent-dark); border-color: var(--accent-dark); }
/* Autocomplete dropdown */
#loading {
font-size: 14px;
color: #666;
margin-top: 6px;
display: none;
}
#suggestions {
border: 1px solid #ccc;
border-radius: 6px;
max-width: 600px;
margin-top: 6px;
padding: 0;
list-style: none;
background: #fff;
position: absolute;
top: calc(42px + 6px);
left: 0;
width: 100%;
z-index: 10;
display: none;
box-shadow: 0 8px 16px rgba(0,0,0,0.08);
overflow: hidden;
color: #111;
}
#suggestions.show { display: block; }
#suggestions li {
padding: 10px 12px;
cursor: pointer;
line-height: 1.3;
}
#suggestions li:hover,
#suggestions li.selected { background: #f0f6ff; }
.no-suggestions {
color: #666;
font-style: italic;
padding: 10px 12px;
}
/* Dark theme overrides */
html[data-theme="dark"] #queryInput {
background-color: #15171c;
color: #e7e9ee;
border-color: #333;
}
html[data-theme="dark"] #queryInput::placeholder { color: #b3b8c4; }
html[data-theme="dark"] .search-btn {
background: #2d7ed8; border-color: #2d7ed8; color: #fff;
}
html[data-theme="dark"] .search-btn:hover {
background: #2464ac; border-color: #2464ac;
}
html[data-theme="dark"] #suggestions {
background: #15171c;
color: #e7e9ee;
border-color: #333;
box-shadow: 0 8px 16px rgba(0,0,0,0.4);
}
html[data-theme="dark"] #suggestions li { color: #e7e9ee; }
html[data-theme="dark"] #suggestions li:hover,
html[data-theme="dark"] #suggestions li.selected { background: #1d2026; }
html[data-theme="dark"] .no-suggestions { color: var(--text-muted); }
html[data-theme="dark"] #loading { color: var(--text-muted); }
.meta-count { margin-bottom: 24px; color: #333; }
html[data-theme="dark"] .meta-count { color: var(--text); }
/* Result cards */
.result-card {
background: var(--bg);
border-radius: 12px;
box-shadow: 0 4px 12px var(--shadow);
padding: 20px;
margin-bottom: 30px;
transition: 0.25s ease;
}
.result-card:hover { transform: translateY(-2px); box-shadow: 0 6px 18px var(--shadow-strong); }
.video-title { font-size: 20px; font-weight: 700; color: #333; margin-bottom: 6px; line-height: 1.25; word-break: break-word; }
html[data-theme="dark"] .video-title { color: var(--text); }
.timestamp { font-size: 14px; color: #666; margin-bottom: 8px; }
html[data-theme="dark"] .timestamp { color: var(--text-muted); }
.thumb-wrap { margin: 10px 0 6px; }
.thumbnail { width: 320px; max-width: 100%; height: auto; border-radius: 8px; border: 1px solid var(--border); }
.jump-link {
display: inline-block;
margin-top: 8px;
padding: 10px 16px;
background-color: var(--accent);
color: #fff;
text-decoration: none;
border-radius: 6px;
font-weight: 600;
}
.jump-link:hover { background-color: var(--accent-dark); }
.context-block {
background: var(--bg-soft);
border-left: 4px solid var(--accent);
padding: 12px;
margin-top: 14px;
white-space: pre-wrap;
font-size: 15px;
line-height: 1.5;
word-wrap: break-word;
}
.summary { font-style: italic; margin-top: 10px; color: var(--text-muted); word-wrap: break-word; }
mark { background-color: #fff59e; padding: 0 2px; border-radius: 2px; }
/* Pagination */
.show-more-form { text-align: center; margin-top: 30px; }
.show-more-form button {
padding: 12px 20px;
font-size: 16px;
background-color: var(--good);
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
}
.show-more-form button:hover { background-color: var(--good-dark); }
/* 💡 Did you mean? suggestion */
.suggestion-box {
background: var(--warn-bg);
border-left: 4px solid var(--warn-accent);
padding: 12px 20px;
margin-bottom: 20px;
font-size: 15px;
color: var(--warn-text);
}
.suggestion-box form { display: inline; }
.suggestion-box button {
background: none;
border: none;
color: var(--accent);
font-weight: 700;
text-decoration: underline;
cursor: pointer;
padding: 0;
font-size: 15px;
}
.suggestion-box button:hover { color: var(--accent-dark); }
/* Theme toggle button */
.theme-toggle {
position: fixed; top: 16px; right: 16px;
background: var(--bg-soft); color: var(--text);
border: 1px solid #00000022; padding: 8px 12px; border-radius: 8px;
font-weight: 600; cursor: pointer; box-shadow: 0 4px 12px var(--shadow);
}
.theme-toggle:hover { box-shadow: 0 6px 18px var(--shadow-strong); }
/* ✅Semantic toggle button */
.semantic-btn {
padding: 6px 12px;
border-radius: 8px;
border: 1px solid var(--border);
background: var(--bg-soft);
color: var(--text);
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: background-color 0.2s, color 0.2s, border-color 0.2s;
}
.semantic-btn.active {
background: var(--accent);
color: #fff;
border-color: var(--accent);
}
html[data-theme="dark"] .semantic-btn {
background: var(--bg-soft);
color: var(--text);
border-color: var(--border);
}
html[data-theme="dark"] .semantic-btn.active {
background: var(--accent);
color: #fff;
border-color: var(--accent);
}
</style>
<!-- Theme toggle logic -->
<script>
(function() {
const saved = localStorage.getItem('theme');
if (saved) document.documentElement.dataset.theme = saved;
function setTheme(t) {
document.documentElement.dataset.theme = t;
localStorage.setItem('theme', t);
const btn = document.getElementById('themeToggle');
if (btn) btn.textContent = t === 'dark' ? ' Light' : ' Dark';
}
window.__toggleTheme = function() {
const next = (document.documentElement.dataset.theme === 'dark') ? 'light' : 'dark';
setTheme(next);
};
document.addEventListener('DOMContentLoaded', () => {
const cur = document.documentElement.dataset.theme || 'light';
const btn = document.getElementById('themeToggle');
if (btn) btn.textContent = cur === 'dark' ? ' Light' : ' Dark';
});
})();
</script>
</head>
<body>
<button id="themeToggle" class="theme-toggle" onclick="__toggleTheme()"> Dark</button>
<!-- 🔎 Search bar at top -->
<form action="/search" method="POST" autocomplete="off" role="search" class="search-wrap">
<div class="search-row">
<input
type="text"
name="query"
id="queryInput"
placeholder="Enter your query here (e.g., neural networks)"
size="50"
value="{{ query }}"
aria-label="Search input"
aria-autocomplete="list"
aria-controls="suggestions"
aria-expanded="false"
aria-haspopup="listbox">
<button type="submit" class="search-btn">Search</button>
</div>
<div id="loading" aria-live="polite">Loading suggestions…</div>
<ul id="suggestions" role="listbox" aria-labelledby="queryInput"></ul>
</form>
<h1>🔍 Search Results for: “{{ query }}”</h1>
<p class="meta-count">
<strong>Showing {{ shown }} of {{ total_matches }} results.</strong>
<!-- Semantic toggle button -->
<form method="POST" action="/search" style="display:inline; margin-left:10px;">
<input type="hidden" name="query" value="{{ query }}">
<input type="hidden" name="start" value="0">
<input type="hidden" name="shown" value="0">
<input type="hidden" name="previous_results" value="[]">
<button type="submit"
name="semantic"
value="{% if semantic %}false{% else %}true{% endif %}"
class="semantic-btn {% if semantic %}active{% endif %}">
Semantic: {% if semantic %}ON{% else %}OFF{% endif %}
</button>
</form>
{% if semantic %}
<span class="semantic-note">
Semantic mode may return results without the exact words but with similar meaning.
</span>
{% endif %}
</p>
<!-- Did you mean? -->
{% if suggestion_term %}
<div class="suggestion-box" role="note" aria-live="polite">
💡 Looking for
<form method="POST" action="/search">
<input type="hidden" name="query" value="{{ suggestion_term }}">
<input type="hidden" name="shown" value="0">
<input type="hidden" name="start" value="0">
<input type="hidden" name="previous_results" value="[]">
{% if semantic %}
<input type="hidden" name="semantic" value="true">
{% endif %}
<button type="submit">“{{ suggestion_term }}”</button>
</form>?
Try searching that too!
</div>
{% endif %}
<!-- Results loop -->
{% for result in results %}
<div class="result-card">
<div class="video-title">{{ result.video_title }}</div>
{% if result.video_id and result.video_id != 'unknown' %}
<div class="thumb-wrap">
<img
class="thumbnail"
loading="lazy"
src="https://img.youtube.com/vi/{{ result.video_id }}/hqdefault.jpg"
alt="Thumbnail for {{ result.video_title }}"
onerror="this.onerror=null;this.src='https://via.placeholder.com/320x180?text=Thumbnail+Not+Available';">
</div>
<div class="timestamp">
⏱️ <time datetime="{{ result.timestamp }}">{{ result.timestamp }}</time>
</div>
<a
class="jump-link"
href="https://www.youtube.com/watch?v={{ result.video_id }}&t={{ result.timestamp|jump_time }}s"
target="_blank"
rel="noopener">▶ Jump to Video</a>
{% else %}
<div style="color:#b00020;">⚠️ No video ID found. Cannot jump to video.</div>
{% endif %}
<div class="context-block">{{ result.highlighted_block }}</div>
<div class="summary"> Summary: {{ result.summary }}</div>
</div>
{% endfor %}
<!-- Pagination -->
{% if start < total_matches %}
<div class="show-more-form">
<form method="POST" action="/search">
<input type="hidden" name="query" value="{{ query }}">
<input type="hidden" name="start" value="{{ start }}">
<input type="hidden" name="shown" value="{{ shown }}">
<input type="hidden" name="previous_results" value='{{ previous_results | tojson | safe }}'>
{% if semantic %}
<input type="hidden" name="semantic" value="true">
{% endif %}
<button type="submit"> Show More Results</button>
</form>
</div>
{% endif %}
<br>
<a href="/">← Back to Search</a>
</body>
</html>