diff --git a/README.md b/README.md index abe669f..60937eb 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,10 @@ Hilfs‑Web‑App für deutsche Wordle‑Rätsel. Nutzer geben bekannte Buchstab ## Features - Filter nach Positionen (1–5), enthaltenen und ausgeschlossenen Buchstaben +- Enthaltene/Ausgeschlossene Buchstaben per Ein‑Zeichen‑Eingabe und „+“-Button hinzufügen +- Ausgewählte Buchstaben werden als Badges angezeigt, Klick entfernt den Buchstaben wieder +- Alphabetische Sortierung der ausgewählten Buchstaben (deutsche Locale) +- Drag‑and‑Drop: Buchstaben aus „Enthalten“ direkt auf die Felder `pos1`–`pos5` ziehen - Deutsche Wortliste (nur 5 Buchstaben), aus OpenThesaurus und wordfreq gemerged - Quellen‑Badges je Treffer (OT/WF) - Zugängliche UI (A11y: Fieldset/Legend, ARIA‑Hinweise, Skip‑Link, semantische Liste) diff --git a/templates/index.html b/templates/index.html index 450ab22..63df01b 100644 --- a/templates/index.html +++ b/templates/index.html @@ -40,15 +40,18 @@ .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; font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', 'Monaco', 'Courier New', monospace; text-transform: uppercase; } .text-input { font-size: 1.1rem; padding: .5rem; background: var(--input-bg); color: var(--input-text); border: 1px solid var(--border); border-radius: .375rem; width: 100%; box-sizing: border-box; font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', 'Monaco', 'Courier New', monospace; text-transform: uppercase; } + .letter-input { width: calc(1ch + 2rem); padding: .5rem 1rem; font-size: 1rem; line-height: 1; background: var(--input-bg); color: var(--input-text); border: 1px solid var(--border); border-radius: .5rem; text-align: center; text-transform: uppercase; font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', 'Monaco', 'Courier New', monospace; } + .plus-button { width: calc(1ch + 2rem); text-align: center; line-height: 1; display: inline-flex; align-items: center; justify-content: center; font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', 'Monaco', 'Courier New', monospace; } label { font-weight: 600; display: block; margin-top: 1rem; margin-bottom: .25rem; } .results { margin-top: 1.5rem; } .results-box { border: 1px solid var(--border); border-radius: .5rem; padding: .75rem; } .badge { display: inline-block; padding: .25rem .5rem; background: var(--badge-bg); color: var(--badge-text); border-radius: .375rem; margin-right: .25rem; margin-bottom: .25rem; } + #includes-list .badge, #excludes-list .badge { cursor: pointer; } .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; margin-right: 0.5rem; } - .reset-button { background: var(--muted); color: var(--bg); } + button { margin-top: 1rem; padding: .5rem 1rem; font-size: 1rem; margin-right: 0.5rem; background: var(--button-bg); color: var(--button-text); border: 1px solid var(--border); border-radius: .5rem; cursor: pointer; } + .reset-button { background: var(--muted); } summary { cursor: pointer; } .footer { margin-top: 2rem; font-size: .9rem; color: var(--muted); } .footer a { color: inherit; text-decoration: underline; } @@ -59,11 +62,14 @@ .inline-controls { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; } .filter-box { border: 1px solid var(--border); border-radius: .5rem; padding: .5rem .75rem; margin: .5rem 0 1rem; } .word-list { list-style: none; padding: 0; margin: 0; } + #includes-list, #excludes-list { margin-top: .75rem; } .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; } + .inline-controls .plus-button { margin-top: 0; margin-right: 0; } + .drop-target { outline: 2px dashed #3b82f6; outline-offset: 2px; } @@ -91,13 +97,23 @@

Je Feld genau ein Buchstabe. Umlaute (ä, ö, ü) und ß sind erlaubt.

- - -

Mehrere Buchstaben ohne Trennzeichen eingeben (z. B. „aei“).

+ +
+ + + +
+ +

Gib einen Buchstaben ein und füge ihn mit „+“ zur Liste hinzu.

- - -

Buchstaben, die nicht vorkommen (z. B. „rst“).

+ +
+ + + +
+ +

Gib einen Buchstaben ein und füge ihn mit „+“ zur Liste hinzu.

@@ -201,6 +217,64 @@ var countEl = document.getElementById('visible-count'); if (countEl) countEl.textContent = String(visible); } + function updateLettersList(listElementId, lettersString) { + var ul = document.getElementById(listElementId); + if (!ul) return; + while (ul.firstChild) ul.removeChild(ul.firstChild); + var letters = (lettersString || '') + .split('') + .filter(function(ch){ return !!ch; }) + .map(function(ch){ return ch.toLowerCase(); }) + .sort(function(a,b){ return a.localeCompare(b, 'de'); }); + letters.forEach(function(ch){ + var li = document.createElement('li'); + var badge = document.createElement('span'); + badge.className = 'badge'; + var lower = ch.toLowerCase(); + badge.textContent = lower.toUpperCase(); + badge.setAttribute('data-letter', lower); + badge.title = 'Zum Entfernen klicken'; + badge.setAttribute('aria-label', "Buchstabe '" + lower.toUpperCase() + "' entfernen"); + // Drag & Drop für Includes-Liste aktivieren + if (listElementId === 'includes-list') { + badge.draggable = true; + badge.addEventListener('dragstart', function(e){ + try { + e.dataTransfer.setData('text/plain', lower); + e.dataTransfer.setData('text/list', listElementId); + e.dataTransfer.effectAllowed = 'move'; + } catch (err) {} + }); + } + li.appendChild(badge); + ul.appendChild(li); + }); + } + function removeLetterFromList(hiddenId, listId, letter) { + var hidden = document.getElementById(hiddenId); + if (!hidden) return; + var current = hidden.value || ''; + var next = (current || '').split('').filter(function(ch){ return ch !== letter; }).join(''); + hidden.value = next; + updateLettersList(listId, next); + } + function addLetterFromInput(inputId, hiddenId, listId) { + var input = document.getElementById(inputId); + var hidden = document.getElementById(hiddenId); + if (!input || !hidden) return; + var raw = (input.value || '').trim(); + if (!raw) return; + var ch = raw[0]; + if (!/[A-Za-zÄÖÜäöüß]/.test(ch)) { input.value = ''; return; } + var lower = ch.toLowerCase(); + var current = hidden.value || ''; + if (current.indexOf(lower) === -1) { + hidden.value = current + lower; + updateLettersList(listId, hidden.value); + } + input.value = ''; + input.focus(); + } window.addEventListener('load', function() { var ot = document.getElementById('filter-ot'); var wf = document.getElementById('filter-wf'); @@ -209,6 +283,62 @@ if (wf) wf.addEventListener('change', applyFilters); if (uml) uml.addEventListener('change', applyFilters); applyFilters(); + // Listen initial aus Hidden-Feldern rendern + var hiddenInc = document.getElementById('includes'); + var hiddenExc = document.getElementById('excludes'); + updateLettersList('includes-list', hiddenInc ? hiddenInc.value : ''); + updateLettersList('excludes-list', hiddenExc ? hiddenExc.value : ''); + // Add-Buttons verdrahten + var addIncBtn = document.getElementById('includes-add-button'); + if (addIncBtn) addIncBtn.addEventListener('click', function(){ addLetterFromInput('includes-input-one', 'includes', 'includes-list'); }); + var addExcBtn = document.getElementById('excludes-add-button'); + if (addExcBtn) addExcBtn.addEventListener('click', function(){ addLetterFromInput('excludes-input-one', 'excludes', 'excludes-list'); }); + // Enter-Handling in den Ein-Feld-Eingaben + var incInputOne = document.getElementById('includes-input-one'); + if (incInputOne) incInputOne.addEventListener('keydown', function(e){ if (e.key === 'Enter') { e.preventDefault(); addLetterFromInput('includes-input-one', 'includes', 'includes-list'); } }); + var excInputOne = document.getElementById('excludes-input-one'); + if (excInputOne) excInputOne.addEventListener('keydown', function(e){ if (e.key === 'Enter') { e.preventDefault(); addLetterFromInput('excludes-input-one', 'excludes', 'excludes-list'); } }); + // Entfernen per Klick auf Badge (Event Delegation) + var incList = document.getElementById('includes-list'); + if (incList) incList.addEventListener('click', function(e){ + var t = e.target; + if (t && t.classList && t.classList.contains('badge')) { + var letter = (t.getAttribute('data-letter') || t.textContent || '').trim().toLowerCase(); + if (letter) removeLetterFromList('includes', 'includes-list', letter); + } + }); + var excList = document.getElementById('excludes-list'); + if (excList) excList.addEventListener('click', function(e){ + var t = e.target; + if (t && t.classList && t.classList.contains('badge')) { + var letter = (t.getAttribute('data-letter') || t.textContent || '').trim().toLowerCase(); + if (letter) removeLetterFromList('excludes', 'excludes-list', letter); + } + }); + // Drop-Ziele für Positionsfelder (pos1..pos5) + for (var i = 1; i <= 5; i++) { + (function(idx){ + var input = document.getElementById('pos' + idx); + if (!input) return; + input.addEventListener('dragover', function(e){ + e.preventDefault(); + try { e.dataTransfer.dropEffect = 'move'; } catch (err) {} + input.classList.add('drop-target'); + }); + input.addEventListener('dragleave', function(){ input.classList.remove('drop-target'); }); + input.addEventListener('drop', function(e){ + e.preventDefault(); + input.classList.remove('drop-target'); + var letter = (e.dataTransfer.getData('text/plain') || '').trim().toLowerCase(); + var sourceList = e.dataTransfer.getData('text/list') || ''; + if (letter && sourceList === 'includes-list' && /[a-zäöüß]/i.test(letter)) { + input.value = letter; + removeLetterFromList('includes', 'includes-list', letter); + input.focus(); + } + }); + })(i); + } // Reset-Button Funktionalität var resetButton = document.getElementById('reset-button'); @@ -223,9 +353,17 @@ // Weitere Felder zurücksetzen var includesField = document.getElementById('includes'); if (includesField) includesField.value = ''; + var includesList = document.getElementById('includes-list'); + if (includesList) includesList.innerHTML = ''; + var incInput = document.getElementById('includes-input-one'); + if (incInput) incInput.value = ''; var excludesField = document.getElementById('excludes'); if (excludesField) excludesField.value = ''; + var excludesList = document.getElementById('excludes-list'); + if (excludesList) excludesList.innerHTML = ''; + var excInput = document.getElementById('excludes-input-one'); + if (excInput) excInput.value = ''; // Suchergebnisse ausblenden var resultsDiv = document.getElementById('results');