feat: settings page — PipeWire audio device + remote Whisper/Ollama config
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
const token = sessionStorage.getItem('token');
|
||||
function authHeaders() {
|
||||
return token ? { 'Authorization': 'Bearer ' + token } : {};
|
||||
}
|
||||
function apiFetch(url, options) {
|
||||
options = options || {};
|
||||
return fetch(url, Object.assign({}, options, {
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, authHeaders(), options.headers || {}),
|
||||
}));
|
||||
}
|
||||
|
||||
let _devices = [];
|
||||
|
||||
function showToast(msg) {
|
||||
const t = document.getElementById('toast');
|
||||
t.textContent = msg;
|
||||
t.classList.add('show');
|
||||
setTimeout(function() { t.classList.remove('show'); }, 2500);
|
||||
}
|
||||
|
||||
async function loadDevices() {
|
||||
const r = await apiFetch('/audio/devices');
|
||||
if (!r.ok) return;
|
||||
_devices = await r.json();
|
||||
const sel = document.getElementById('audio-device');
|
||||
const current = sel.value;
|
||||
sel.replaceChildren(new Option('Systemstandard', ''));
|
||||
_devices.forEach(function(d) { sel.appendChild(new Option(d.name, d.name)); });
|
||||
if (current) sel.value = current;
|
||||
['combined-mic', 'combined-monitor'].forEach(function(id) {
|
||||
const el = document.getElementById(id);
|
||||
el.replaceChildren();
|
||||
_devices.forEach(function(d) { el.appendChild(new Option(d.name, d.name)); });
|
||||
});
|
||||
}
|
||||
|
||||
async function loadOllamaModels(baseUrl, current) {
|
||||
try {
|
||||
const r = await fetch(baseUrl + '/api/tags');
|
||||
if (!r.ok) return;
|
||||
const data = await r.json();
|
||||
const sel = document.getElementById('ollama-model');
|
||||
sel.replaceChildren();
|
||||
(data.models || []).forEach(function(m) { sel.appendChild(new Option(m.name, m.name)); });
|
||||
if (current) sel.value = current;
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
async function loadConfig() {
|
||||
const r = await apiFetch('/config');
|
||||
if (!r.ok) return;
|
||||
const cfg = await r.json();
|
||||
document.getElementById('audio-device').value = (cfg.audio && cfg.audio.device) || '';
|
||||
document.getElementById('whisper-url').value = (cfg.whisper && cfg.whisper.base_url) || '';
|
||||
document.getElementById('whisper-model').value = (cfg.whisper && cfg.whisper.model) || 'large-v3';
|
||||
const ollamaUrl = (cfg.ollama && cfg.ollama.base_url) || 'http://localhost:11434';
|
||||
document.getElementById('ollama-url').value = ollamaUrl;
|
||||
await loadOllamaModels(ollamaUrl, cfg.ollama && cfg.ollama.model);
|
||||
}
|
||||
|
||||
document.getElementById('refresh-devices-btn').addEventListener('click', loadDevices);
|
||||
|
||||
document.getElementById('create-combined-btn').addEventListener('click', function() {
|
||||
document.getElementById('combined-form').classList.toggle('visible');
|
||||
});
|
||||
document.getElementById('combined-cancel-btn').addEventListener('click', function() {
|
||||
document.getElementById('combined-form').classList.remove('visible');
|
||||
});
|
||||
document.getElementById('combined-confirm-btn').addEventListener('click', async function() {
|
||||
const mic = document.getElementById('combined-mic').value;
|
||||
const monitor = document.getElementById('combined-monitor').value;
|
||||
const r = await apiFetch('/audio/combined', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ mic: mic, monitor: monitor }),
|
||||
});
|
||||
if (!r.ok) { showToast('Fehler beim Erstellen'); return; }
|
||||
const data = await r.json();
|
||||
showToast('Erstellt: ' + data.device);
|
||||
document.getElementById('combined-form').classList.remove('visible');
|
||||
await loadDevices();
|
||||
document.getElementById('audio-device').value = data.device;
|
||||
});
|
||||
|
||||
document.getElementById('ollama-url').addEventListener('change', function(e) {
|
||||
loadOllamaModels(e.target.value, document.getElementById('ollama-model').value);
|
||||
});
|
||||
|
||||
document.getElementById('save-btn').addEventListener('click', async function() {
|
||||
const body = {
|
||||
audio: { device: document.getElementById('audio-device').value },
|
||||
whisper: {
|
||||
base_url: document.getElementById('whisper-url').value,
|
||||
model: document.getElementById('whisper-model').value,
|
||||
},
|
||||
ollama: {
|
||||
base_url: document.getElementById('ollama-url').value,
|
||||
model: document.getElementById('ollama-model').value,
|
||||
},
|
||||
};
|
||||
const r = await apiFetch('/config', { method: 'PUT', body: JSON.stringify(body) });
|
||||
if (r.ok) { showToast('Gespeichert'); } else { showToast('Fehler beim Speichern'); }
|
||||
});
|
||||
|
||||
(async function() {
|
||||
if (!token) { location.href = '/login'; return; }
|
||||
await loadDevices();
|
||||
await loadConfig();
|
||||
})();
|
||||
Reference in New Issue
Block a user