Files
wordle-cheater/templates/index.html

197 lines
11 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>WordleCheater (DE)</title>
<meta name="description" content="WordleCheater: Finde deutsche 5BuchstabenWörter anhand bekannter Buchstaben und Positionen. Quellen: OpenThesaurus und wordfreq." />
<meta name="robots" content="index,follow" />
<link rel="canonical" href="{{ request.url_root }}" />
<meta property="og:type" content="website" />
<meta property="og:title" content="WordleCheater (DE)" />
<meta property="og:description" content="Finde deutsche 5BuchstabenWörter mit Positions- und Buchstabenfiltern. Quellen: OpenThesaurus & wordfreq." />
<meta property="og:url" content="{{ request.url_root }}" />
<meta name="twitter:card" content="summary" />
<meta name="twitter:title" content="WordleCheater (DE)" />
<meta name="twitter:description" content="Finde deutsche 5BuchstabenWörter mit Positions- und Buchstabenfiltern. Quellen: OpenThesaurus & wordfreq." />
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='favicon.svg') }}" />
<link rel="manifest" href="/manifest.webmanifest" />
<meta name="theme-color" content="#0b1220" />
<meta name="color-scheme" content="light dark" />
<script>
(function() {
try {
var saved = localStorage.getItem('theme');
var prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
var theme = saved || (prefersDark ? 'dark' : 'light');
document.documentElement.setAttribute('data-theme', theme);
} catch (e) {}
})();
</script>
<style>
:root {
--bg: #ffffff;
--text: #111827;
--muted: #6b7280;
--badge-bg: #e5e7eb;
--badge-text: #111827;
--border: #e5e7eb;
--skip-bg: #111827;
--skip-text: #ffffff;
--button-bg: #111827;
--button-text: #ffffff;
--input-bg: #ffffff;
--input-text: #111827;
}
[data-theme="dark"] {
--bg: #0b1220;
--text: #e5e7eb;
--muted: #9ca3af;
--badge-bg: #374151;
--badge-text: #f9fafb;
--border: #334155;
--skip-bg: #e5e7eb;
--skip-text: #111827;
--button-bg: #e5e7eb;
--button-text: #111827;
--input-bg: #111827;
--input-text: #e5e7eb;
}
html, body { background: var(--bg); color: var(--text); }
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; margin: 2rem; }
.container { max-width: 800px; margin: 0 auto; }
.page-header { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; }
.page-header h1 { margin: 0; }
@media (max-width: 480px) {
.page-header h1 { font-size: 1.5rem; }
}
.grid { display: grid; grid-template-columns: repeat(5, 3rem); gap: .5rem; }
.grid input { text-align: center; font-size: 1.25rem; padding: .4rem; background: var(--input-bg); color: var(--input-text); border: 1px solid var(--border); border-radius: .375rem; }
label { font-weight: 600; display: block; margin-top: 1rem; margin-bottom: .25rem; }
.results { margin-top: 1.5rem; }
.badge { display: inline-block; padding: .25rem .5rem; background: var(--badge-bg); color: var(--badge-text); border-radius: .375rem; margin-right: .25rem; margin-bottom: .25rem; }
.source { font-size: .75rem; padding: .1rem .35rem; border-radius: .25rem; margin-left: .25rem; }
.source.ot { background: #dbeafe; color: #1e40af; }
.source.wf { background: #dcfce7; color: #065f46; }
button { margin-top: 1rem; padding: .5rem 1rem; font-size: 1rem; }
summary { cursor: pointer; }
.footer { margin-top: 2rem; font-size: .9rem; color: var(--muted); }
.footer a { color: inherit; text-decoration: underline; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 1px, 1px); white-space: nowrap; border: 0; }
.skip-link { position: absolute; left: -9999px; top: auto; width: 1px; height: 1px; overflow: hidden; }
.skip-link:focus { position: static; width: auto; height: auto; padding: .5rem .75rem; background: var(--skip-bg); color: var(--skip-text); border-radius: .25rem; }
.hint { margin-top: .25rem; color: var(--muted); font-size: .9rem; }
.word-list { list-style: none; padding: 0; margin: 0; }
.word-list li { display: inline-block; margin: 0 .25rem .25rem 0; }
fieldset { border: 1px solid var(--border); border-radius: .5rem; padding: .75rem; }
legend { font-weight: 700; padding: 0 .25rem; }
.theme-toggle { background: var(--button-bg); color: var(--button-text); border: 1px solid var(--border); border-radius: .5rem; padding: .4rem .6rem; font-size: .95rem; margin-top: 0; }
.theme-toggle:focus { outline: 2px solid #3b82f6; outline-offset: 2px; }
</style>
</head>
<body>
<a href="#results" class="skip-link">Zum Ergebnisbereich springen</a>
<div class="container">
<header class="page-header">
<button id="theme-toggle" class="theme-toggle" aria-pressed="false" aria-label="Theme umschalten" title="Theme umschalten">🌞/🌙</button>
<h1>WordleCheater (Deutsch)</h1>
</header>
<p>Wortliste geladen: <strong>{{ words_count }}</strong> Wörter</p>
<main id="main" role="main">
<form method="post" aria-describedby="form-hint">
<p id="form-hint" class="hint">Gib bekannte Buchstaben ein. Leere Felder werden ignoriert.</p>
<fieldset>
<legend>Buchstaben mit korrekter Position</legend>
<div class="grid" aria-describedby="pos-hint">
<input id="pos1" name="pos1" maxlength="1" aria-label="Position 1" inputmode="text" autocomplete="off" pattern="[A-Za-zÄÖÜäöüß]" value="{{ pos[0] }}" />
<input id="pos2" name="pos2" maxlength="1" aria-label="Position 2" inputmode="text" autocomplete="off" pattern="[A-Za-zÄÖÜäöüß]" value="{{ pos[1] }}" />
<input id="pos3" name="pos3" maxlength="1" aria-label="Position 3" inputmode="text" autocomplete="off" pattern="[A-Za-zÄÖÜäöüß]" value="{{ pos[2] }}" />
<input id="pos4" name="pos4" maxlength="1" aria-label="Position 4" inputmode="text" autocomplete="off" pattern="[A-Za-zÄÖÜäöüß]" value="{{ pos[3] }}" />
<input id="pos5" name="pos5" maxlength="1" aria-label="Position 5" inputmode="text" autocomplete="off" pattern="[A-Za-zÄÖÜäöüß]" value="{{ pos[4] }}" />
</div>
<p id="pos-hint" class="hint">Je Feld genau ein Buchstabe. Umlaute (ä, ö, ü) und ß sind erlaubt.</p>
</fieldset>
<label for="includes">Weitere enthaltene Buchstaben (beliebige Reihenfolge)</label>
<input id="includes" name="includes" aria-describedby="includes-hint" inputmode="text" autocomplete="off" value="{{ includes }}" />
<p id="includes-hint" class="hint">Mehrere Buchstaben ohne Trennzeichen eingeben (z.B. „aei“).</p>
<label for="excludes">Ausgeschlossene Buchstaben</label>
<input id="excludes" name="excludes" aria-describedby="excludes-hint" inputmode="text" autocomplete="off" value="{{ excludes }}" />
<p id="excludes-hint" class="hint">Buchstaben, die nicht vorkommen (z.B. „rst“).</p>
<button type="submit">Suchen</button>
</form>
{% if results is not none %}
<div class="results" id="results" role="region" aria-labelledby="results-title">
<h2 id="results-title">Vorschläge ({{ results|length }})</h2>
{% if results|length == 0 %}
<p>Keine Treffer. Bitte Bedingungen anpassen.</p>
{% else %}
<details open>
<summary>Liste anzeigen</summary>
<ul class="word-list">
{% for w in results %}
<li>
<span class="badge">{{ w }}
{% set srcs = sources_map.get(w, []) %}
{% for s in srcs %}
{% if s == 'ot' %}
<span class="source ot">OT</span>
{% elif s == 'wf' %}
<span class="source wf">WF</span>
{% endif %}
{% endfor %}
</span>
</li>
{% endfor %}
</ul>
</details>
{% endif %}
<p>
<strong>Legende:</strong>
<span class="source wf">WF</span> = <a href="https://pypi.org/project/wordfreq/" target="_blank" rel="noopener noreferrer">Wordfreq</a>,
<span class="source ot">OT</span> = <a href="https://www.openthesaurus.de" target="_blank" rel="noopener noreferrer">OpenThesaurus</a>
</p>
</div>
{% endif %}
</main>
<footer class="footer">
Yet another <a href="https://gitea.elpatron.me/elpatron/wordle-cheater" rel="noopener noreferrer">Open Source Project</a>, vibe coded in less than 1 hour by <a href="mailto:elpatron@mailbox.org">Markus F.J. Busche</a>
</footer>
</div>
<script>
(function() {
var btn = document.getElementById('theme-toggle');
if (!btn) return;
function currentTheme() { return document.documentElement.getAttribute('data-theme') || 'light'; }
function setTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
try { localStorage.setItem('theme', theme); } catch (e) {}
btn.setAttribute('aria-pressed', theme === 'dark');
btn.textContent = theme === 'dark' ? '🌙' : '🌞';
btn.title = 'Theme umschalten (' + (theme === 'dark' ? 'Dunkel' : 'Hell') + ')';
}
// init label/state
setTheme(currentTheme());
btn.addEventListener('click', function() {
var next = currentTheme() === 'dark' ? 'light' : 'dark';
setTheme(next);
});
})();
</script>
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js');
});
}
</script>
</body>
</html>