import { renderHeader } from '../components/header.js';
import { saveEntrySettings, state } from '../state.js';
import { speakTextBySettings } from '../services/speech-tools-service.js';
export const pageMeta = { id: 'tools-settings-view', title: 'Настройки инструментов' };
function optionsMarkup(options, selected) {
return options.map((opt) => (
``
)).join('');
}
export function render({ navigate }) {
const screen = document.createElement('section');
screen.className = 'stack';
const stt = state.entrySettings.tools?.speechToText || {};
const tts = state.entrySettings.tools?.textToSpeech || {};
const card = document.createElement('div');
card.className = 'card stack';
card.innerHTML = `
Распознавание речи
`;
const card2 = document.createElement('div');
card2.className = 'card stack';
card2.innerHTML = `
Прочесть вслух (TTS)
Для офлайн-озвучки через Piper используйте локальный HTTP-обёртчик. Кнопка «шаблон» подставляет базовые значения.
`;
const actions = document.createElement('div');
actions.className = 'auth-footer-actions';
actions.innerHTML = `
`;
actions.querySelector('#tools-cancel')?.addEventListener('click', () => navigate('settings-view'));
actions.querySelector('#tools-save')?.addEventListener('click', async () => {
const next = {
...state.entrySettings,
tools: {
speechToText: {
provider: card.querySelector('#stt-provider')?.value || 'openai',
quality: card.querySelector('#stt-quality')?.value || 'medium',
baseUrl: String(card.querySelector('#stt-base-url')?.value || '').trim(),
apiKey: String(card.querySelector('#stt-api-key')?.value || '').trim(),
model: String(card.querySelector('#stt-model')?.value || '').trim(),
},
textToSpeech: {
provider: card2.querySelector('#tts-provider')?.value || 'browser',
quality: card2.querySelector('#tts-quality')?.value || 'medium',
voice: String(card2.querySelector('#tts-voice')?.value || '').trim(),
piperBaseUrl: String(card2.querySelector('#tts-piper-url')?.value || '').trim(),
externalBaseUrl: String(card2.querySelector('#tts-external-url')?.value || '').trim(),
apiKey: String(card2.querySelector('#tts-api-key')?.value || '').trim(),
model: String(card2.querySelector('#tts-model')?.value || '').trim(),
},
},
};
await saveEntrySettings(next);
navigate('settings-view');
});
card2.querySelector('#piper-autofill')?.addEventListener('click', () => {
card2.querySelector('#tts-provider').value = 'piper-http';
card2.querySelector('#tts-piper-url').value = 'http://127.0.0.1:5000';
card2.querySelector('#tts-quality').value = 'medium';
if (!String(card2.querySelector('#tts-voice').value || '').trim()) {
card2.querySelector('#tts-voice').value = 'ru_RU-irina-medium';
}
});
card2.querySelector('#piper-links')?.addEventListener('click', () => {
window.open('https://github.com/rhasspy/piper', '_blank', 'noopener,noreferrer');
window.open('https://huggingface.co/rhasspy/piper-voices/tree/main', '_blank', 'noopener,noreferrer');
});
card2.querySelector('#tts-test-btn')?.addEventListener('click', async () => {
const ttsProvider = card2.querySelector('#tts-provider')?.value || 'openai';
const text = String(card2.querySelector('#tts-test-text')?.value || '').trim();
const runtimeSettings = {
...state.entrySettings,
tools: {
...state.entrySettings.tools,
textToSpeech: {
provider: ttsProvider,
quality: card2.querySelector('#tts-quality')?.value || 'medium',
voice: String(card2.querySelector('#tts-voice')?.value || '').trim(),
piperBaseUrl: String(card2.querySelector('#tts-piper-url')?.value || '').trim(),
externalBaseUrl: String(card2.querySelector('#tts-external-url')?.value || '').trim(),
apiKey: String(card2.querySelector('#tts-api-key')?.value || '').trim(),
model: String(card2.querySelector('#tts-model')?.value || '').trim(),
},
},
};
try {
await speakTextBySettings(text || 'Проверка озвучки', runtimeSettings);
} catch (error) {
window.alert(`Ошибка озвучки: ${error?.message || 'unknown'}`);
}
});
screen.append(
renderHeader({
title: 'Настройки инструментов',
leftAction: { label: '←', onClick: () => navigate('settings-view') },
}),
card,
card2,
actions,
);
return screen;
}