Add spelling improvements and local Piper TTS
Practice: fire streak badge, first-letter hint on wrong answers, missed-words drill session after practice completes. Test: audio-only mode with listening state before input appears, larger re-read button. Words: definition field (DB migration + admin editor + TTS playback). TTS: replace speechSynthesis with local Piper via /api/tts, WAV cached in data/tts-cache, falls back to browser voice when Piper is not installed. Dockerfile: switched to node:26-slim, installs Piper binary + en_US-lessac-medium voice. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
544469d6c9
commit
6f2281e2a7
8 files changed
+202
-35
No files matched your search
+7
-3
@@ -897,7 +897,11 @@ function renderLists(lists) {
|
||||
const list = await api(`/api/lists/${btn.dataset.editList}`);
|
||||
$('#list-id').value = list.builtin ? '' : list.id;
|
||||
$('#list-name').value = list.builtin ? `${list.name} (copy)` : list.name;
|
||||
$('#list-words').value = list.words.map((w) => (w.sentence ? `${w.word} | ${w.sentence}` : w.word)).join('\n');
|
||||
$('#list-words').value = list.words.map((w) => {
|
||||
if (w.definition) return `${w.word} | ${w.sentence} | ${w.definition}`;
|
||||
if (w.sentence) return `${w.word} | ${w.sentence}`;
|
||||
return w.word;
|
||||
}).join('\n');
|
||||
$('#list-editor-title').textContent = list.builtin ? 'New list (from copy)' : `Editing: ${list.name}`;
|
||||
$('#list-editor-details').open = true;
|
||||
$('#list-name').scrollIntoView({ behavior: 'smooth' });
|
||||
@@ -920,8 +924,8 @@ $('#list-form').addEventListener('submit', async (e) => {
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean)
|
||||
.map((line) => {
|
||||
const [word, sentence = ''] = line.split('|').map((p) => p.trim());
|
||||
return { word, sentence };
|
||||
const [word, sentence = '', definition = ''] = line.split('|').map((p) => p.trim());
|
||||
return { word, sentence, definition };
|
||||
});
|
||||
if (!name || words.length === 0) return msg('Give the list a name and at least one word.');
|
||||
const id = $('#list-id').value;
|
||||
|
||||
Reference in new issue
Block a user