Initial commit

This commit is contained in:
2025-12-25 12:01:35 +03:00
commit c574533f63
85 changed files with 6027 additions and 0 deletions
+197
View File
@@ -0,0 +1,197 @@
import { getSettings, updateSettings, restart } from "../api.js";
export async function loadSettings() {
const content = document.getElementById("content");
if (!content) {
console.error("Content element not found");
return;
}
// Показать индикатор загрузки
content.innerHTML = '<div class="loading">Loading settings...</div>';
try {
const settingsHtml = await fetch("pages/settings.html").then((res) =>
res.text()
);
content.innerHTML = settingsHtml;
const allSettings = await getSettings();
// Заполнение форм
fillForm("vad-form", allSettings.vad);
fillForm("system-form", allSettings.system);
fillForm("stt-form", allSettings.stt);
fillForm("commands-form", allSettings.commands);
fillForm("auth-form", allSettings.auth);
fillForm("tts-form", allSettings.tts);
fillForm("audio-settings-form", allSettings.audio_settings);
// Инициализация аккордеона
initAccordion();
// Обработчики кнопок сохранения
document.querySelectorAll(".save-btn").forEach((btn) => {
btn.addEventListener("click", async () => {
const section = btn.dataset.section;
const formId = section.replace('_', '-') + '-form';
const form = document.getElementById(formId);
if (!form) {
console.error(`Form ${formId} not found`);
alert(`Form for ${section} not found`);
return;
}
// Собираем данные формы вручную
const formData = {};
// Обрабатываем все поля ввода
form.querySelectorAll("input, select").forEach((input) => {
if (input.type === "checkbox") {
formData[input.name] = input.checked;
} else if (input.value !== "") {
formData[input.name] = input.value;
}
});
// Выводим данные в консоль для отладки
console.log("Отправляемые данные:", formData);
try {
await updateSettings(section, formData);
alert(
`${
section.charAt(0).toUpperCase() + section.slice(1)
} settings saved successfully`
);
} catch (error) {
console.error(`Failed to save ${section} settings:`, error);
alert(`Failed to save ${section} settings`);
}
});
});
// Обработчик переинициализации VAD
const restartBtn = document.getElementById("restart-btn");
if (restartBtn) {
restartBtn.addEventListener("click", async () => {
if (confirm("Are you sure you want to restart VAD?")) {
try {
await restart();
alert("VAD restarted successfully");
} catch (error) {
console.error("Failed to restart VAD:", error);
alert("Failed to restart VAD");
}
}
});
}
} catch (error) {
console.error("Error loading settings:", error);
content.innerHTML =
'<div class="error">Failed to load settings. Check console for details.</div>';
}
}
// Функция инициализации аккордеона
function initAccordion() {
// Получаем все заголовки секций
const sectionHeaders = document.querySelectorAll(".section-header");
// Добавляем обработчик клика для каждого заголовка
sectionHeaders.forEach((header) => {
header.addEventListener("click", function () {
// Получаем содержимое секции
const content = this.nextElementSibling;
// Если секция уже активна, просто сворачиваем её
if (!this.classList.contains("collapsed")) {
this.classList.add("collapsed");
content.classList.remove("active");
return;
}
// Сворачиваем все секции
document.querySelectorAll(".section-header").forEach((h) => {
h.classList.add("collapsed");
h.nextElementSibling.classList.remove("active");
});
// Разворачиваем текущую секцию
this.classList.remove("collapsed");
content.classList.add("active");
});
});
}
function fillForm(formId, data) {
const form = document.getElementById(formId);
if (!form) {
console.warn(`Form ${formId} not found`);
return;
}
for (const [key, value] of Object.entries(data)) {
const input = form.querySelector(`[name="${key}"]`);
if (input) {
if (input.type === "checkbox") {
input.checked = !!value; // Приводим к булевому типу
} else {
input.value = value !== undefined ? value : "";
}
} else {
console.warn(`Input for ${key} not found in ${formId}`);
}
}
}
function processFormData(section, formData) {
const data = {};
for (let [key, value] of formData) {
if (value === "") continue; // Пропускаем пустые значения
switch (key) {
case "mode":
case "sample_rate":
case "frame_duration_ms":
case "ring_buffer_size":
case "energy_threshold":
case "min_speech_duration_ms":
case "max_record_duration_sec":
case "target_rms":
case "max_gain":
case "history_maxlen":
case "polling_interval_ms":
case "port":
case "timeout":
case "playback_device":
case "capture_device":
case "playback_volume":
case "capture_volume":
data[key] = parseInt(value);
break;
case "speech_threshold":
case "record_stop_delay_sec":
case "prebuffer_duration_sec":
case "noise_reduction_factor":
case "noise_update_alpha":
case "min_rms_threshold_factor":
case "gain_smoothing_alpha":
case "adaptive_energy_std_factor":
case "pause_duration":
case "post_stop_wait":
data[key] = parseFloat(value);
break;
case "adaptive_energy":
case "noise_reduction_enabled":
case "auto_gain_enabled":
case "record_on_speech":
case "log_save_file":
case "save_TTS_files":
data[key] = value === "on"; // Checkbox возвращает 'on' при checked или 'off' при unchecked
break;
default:
data[key] = value;
}
}
return data;
}