diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bdb646..01a9bb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,9 @@ - The settings page is split into tabs: **Look, Models, Workflows, Prompts, Backup, Help**. "Change where downloads go" opens Models directly. - **Where downloads (and now imports) go is a choice**: by base model (the new default), in Downloads then by base model, all in a Downloads folder, straight in the type's folder, or your own folder typed by hand; an example path shows the result. Without a saved setting, downloads now go by base model instead of into a Downloads folder. +### Fixes +- **Edit emotions: every clip in the list, and the line follows the clip (编辑情绪:下拉列出全部音频,换音频台词跟着换)**: a box that already held a file listed only the files whose names contain it (the browser filters by the box's text), so a character's other clips seemed missing. Pressing the box now lists all of them, with the current file shown greyed; leaving without a pick keeps it. Picking another clip no longer keeps the old clip's line beside it: the line the node already reads for that clip is filled in, otherwise the box is left blank and the node reads the clip's own line (its .txt / .lab or the annotation list) when it speaks; going back to the first clip brings its line back. + ## v1.59.0 Beta (Model Downloads, Backup, Share Codes, Node Combos, MCP, Light Theme) — unreleased ### ⬇️ Download missing models from Model Check (模型检查一键下载缺失模型) diff --git a/web/modules/ui_audio_tts_editor.js b/web/modules/ui_audio_tts_editor.js index 31f15ee..8e6eaff 100644 --- a/web/modules/ui_audio_tts_editor.js +++ b/web/modules/ui_audio_tts_editor.js @@ -39,6 +39,24 @@ function audioInput(listId, value) { input.setAttribute('list', listId); input.placeholder = t('ttsEditorAudioPlaceholder'); input.value = value || ''; + // The browser lists only the files matching the box's text, so a filled box offered one or + // two. Pressing it empties the box to list them all (the file shows greyed meanwhile); + // leaving without a pick puts the file back, unless it was deleted with a key. + let kept = ''; + input.addEventListener('mousedown', () => { + if (!input.value) return; + kept = input.value; + input.placeholder = kept; + input.value = ''; + }); + input.addEventListener('keydown', (e) => { + if (e.key === 'Backspace' || e.key === 'Delete') kept = ''; + }); + input.addEventListener('blur', () => { + if (!input.value) input.value = kept; + kept = ''; + input.placeholder = t('ttsEditorAudioPlaceholder'); + }); return input; } @@ -82,6 +100,19 @@ export function openGptSovitsEditor(group, { onSaved } = {}) { close(); }, true); + // A picked file gets its own line: the one it had in this dialog, the line the node read for it + // when it is in use already, or blank, which the node fills from the file's .txt / .lab / list. + const knownText = new Map([raw.reference, ...Object.values(raw.emotions || {})] + .filter(ref => ref?.audio && ref.text).map(ref => [ref.audio, ref.text])); + const linkText = (audio, text) => { + const start = { audio: audio.value.trim(), text: text.value }; + audio.addEventListener('input', () => { + const path = audio.value.trim(); + if (!audioList.includes(path)) return; + text.value = path === start.audio ? start.text : knownText.get(path) || ''; + }); + }; + const play = (input) => { player?.pause(); const path = input.value.trim(); @@ -107,6 +138,7 @@ export function openGptSovitsEditor(group, { onSaved } = {}) { const mainRow = el('div', 'anomalous-tts-row is-main'); const mainAudio = audioInput(listId, settings.reference?.audio); const mainText = textInput(settings.reference?.text); + linkText(mainAudio, mainText); mainRow.append( el('span', 'anomalous-tts-row-name', '{main}'), mainAudio, @@ -127,6 +159,7 @@ export function openGptSovitsEditor(group, { onSaved } = {}) { nameInput.value = row.name || ''; const audio = audioInput(listId, row.audio); const text = textInput(row.text); + linkText(audio, text); line.append( nameInput, audio,