Spaces:
Sleeping
Sleeping
| <!-- Results page (results.html) — shows search results with semantic toggle, autocomplete, and pagination --> | |
| <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> | |