Changeset 0.4.3 (#32)

This commit is contained in:
2026-02-18 17:47:21 +00:00
parent 2fc4f6980c
commit c98eb80950
14 changed files with 1134 additions and 17 deletions

View File

@@ -89,22 +89,27 @@ function updateQuickModelSelector() {
function openSettings() {
updateSettingsUI();
updateProfileSection();
updateProviderSection();
document.getElementById('settingsModal').classList.add('active');
}
function updateProfileSection() {
const profileSection = document.getElementById('profileSection');
const unmanagedSettings = document.getElementById('unmanagedSettings');
const modeBadge = document.getElementById('settingsModeBadge');
if (Backend.isManaged) {
profileSection.style.display = '';
unmanagedSettings.style.display = 'none';
modeBadge.textContent = '🔗 Managed Mode — API keys are configured by your admin or in Providers below';
modeBadge.className = 'settings-mode-badge mode-managed';
loadProfileIntoSettings();
} else {
profileSection.style.display = 'none';
unmanagedSettings.style.display = '';
modeBadge.textContent = '📱 Offline Mode — Configure your own API endpoint and key';
modeBadge.className = 'settings-mode-badge mode-unmanaged';
}
// Always reset password form
document.getElementById('profileChangePwForm').style.display = 'none';
}
@@ -170,6 +175,121 @@ async function handleChangePassword() {
}
}
// ── API Providers (managed mode) ────────────
function updateProviderSection() {
const managed = document.getElementById('managedProviders');
if (Backend.isManaged) {
managed.style.display = '';
loadProviderList();
} else {
managed.style.display = 'none';
}
}
async function loadProviderList() {
const container = document.getElementById('providerList');
container.innerHTML = '<div class="admin-loading">Loading...</div>';
try {
const data = await Backend.listConfigs();
const configs = data.configs || data.data || data || [];
const list = Array.isArray(configs) ? configs : [];
if (list.length === 0) {
container.innerHTML = '<div class="provider-empty">No providers configured. Add one to start chatting.</div>';
return;
}
container.innerHTML = list.map(c => `
<div class="provider-row">
<div class="provider-info">
<span class="provider-name">${escapeHtmlUI(c.name)}</span>
<span class="provider-meta">${c.provider} · ${c.model_default || 'no default'}</span>
</div>
<div class="provider-actions">
<span class="admin-badge admin-badge-active">${c.has_key ? '🔑' : '⚠️'}</span>
${c.user_id ? `<button class="btn btn-small btn-danger" onclick="deleteProvider('${c.id}', '${escapeHtmlUI(c.name)}')">Remove</button>` : '<span class="admin-badge admin-badge-moderator">global</span>'}
</div>
</div>
`).join('');
} catch (e) {
container.innerHTML = '<div class="admin-error">Failed to load providers: ' + e.message + '</div>';
}
}
function showProviderForm() {
document.getElementById('providerAddForm').style.display = '';
document.getElementById('providerShowAddBtn').style.display = 'none';
// Set default endpoint based on provider type
updateProviderEndpointHint();
}
function hideProviderForm() {
document.getElementById('providerAddForm').style.display = 'none';
document.getElementById('providerShowAddBtn').style.display = '';
clearProviderForm();
}
function clearProviderForm() {
document.getElementById('providerName').value = '';
document.getElementById('providerType').value = 'openai';
document.getElementById('providerEndpoint').value = '';
document.getElementById('providerApiKey').value = '';
document.getElementById('providerDefaultModel').value = '';
}
function updateProviderEndpointHint() {
const type = document.getElementById('providerType').value;
const endpoint = document.getElementById('providerEndpoint');
if (!endpoint.value) {
endpoint.placeholder = type === 'anthropic'
? 'https://api.anthropic.com'
: 'https://api.openai.com/v1';
}
}
async function handleCreateProvider() {
const name = document.getElementById('providerName').value.trim();
const provider = document.getElementById('providerType').value;
const endpoint = document.getElementById('providerEndpoint').value.trim();
const apiKey = document.getElementById('providerApiKey').value.trim();
const modelDefault = document.getElementById('providerDefaultModel').value.trim();
if (!name || !endpoint || !apiKey) {
showToast('⚠️ Name, endpoint, and API key are required', 'warning');
return;
}
try {
await Backend.createConfig(name, provider, endpoint, apiKey, modelDefault || null);
showToast('✅ Provider added', 'success');
hideProviderForm();
loadProviderList();
fetchModels(false); // Refresh model list
} catch (e) {
showToast('❌ ' + e.message, 'error');
}
}
async function deleteProvider(configId, name) {
if (!confirm('Remove provider "' + name + '"?')) return;
try {
await Backend.deleteConfig(configId);
showToast('✅ Provider removed', 'success');
loadProviderList();
fetchModels(false);
} catch (e) {
showToast('❌ ' + e.message, 'error');
}
}
function escapeHtmlUI(str) {
const div = document.createElement('div');
div.textContent = str || '';
return div.innerHTML;
}
function closeSettings() {
document.getElementById('settingsModal').classList.remove('active');
}