Providers
@@ -2290,19 +2309,23 @@ function renderTimeouts() {
}
async function updateProvider(name) {
- const prov = {};
- const en = document.getElementById(name+'-enabled');
+ var prov = {};
+ var en = document.getElementById(name+'-enabled');
if (en) prov.enabled = en.checked;
- const url = document.getElementById(name+'-url');
+ var url = document.getElementById(name+'-url');
if (url) prov.base_url = url.value;
- const to = document.getElementById(name+'-timeout');
+ var to = document.getElementById(name+'-timeout');
if (to) prov.timeout = parseInt(to.value) || 120;
- const key = document.getElementById(name+'-key');
+ var key = document.getElementById(name+'-key');
if (key && key.value) prov.api_key = key.value;
- const body = {providers: {}};
+ var body = {providers: {}};
body.providers[name] = prov;
- await fetch('/api/admin/config', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(body)});
- toast('Saved');
+ try {
+ var r = await fetch('/api/admin/config', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(body)});
+ var d = await r.json();
+ if (d.ok) toast(name + ' provider saved', true, en ? (prov.enabled ? 'Enabled' : 'Disabled') : '');
+ else toast('Save failed: ' + (d.error || 'unknown'), false);
+ } catch(e) { toast('Save failed: ' + e.message, false); }
}
async function testProvider(name) {
@@ -2317,13 +2340,17 @@ async function testProvider(name) {
async function toggleOllama(name, enabled) {
config.disabled_models = config.disabled_models || [];
if (enabled) {
- config.disabled_models = config.disabled_models.filter(m => m !== name);
+ config.disabled_models = config.disabled_models.filter(function(m) { return m !== name; });
} else {
if (!config.disabled_models.includes(name)) config.disabled_models.push(name);
}
- await fetch('/api/admin/config', {method:'POST', headers:{'Content-Type':'application/json'},
- body:JSON.stringify({disabled_models: config.disabled_models})});
- toast('Model ' + (enabled ? 'enabled' : 'disabled'));
+ try {
+ var r = await fetch('/api/admin/config', {method:'POST', headers:{'Content-Type':'application/json'},
+ body:JSON.stringify({disabled_models: config.disabled_models})});
+ var d = await r.json();
+ if (d.ok) toast(name + ' ' + (enabled ? 'enabled' : 'disabled'), true);
+ else toast('Failed to save model state', false);
+ } catch(e) { toast('Save error: ' + e.message, false); }
}
function toggleCloud(idx, enabled) {
@@ -2338,9 +2365,13 @@ function removeCloud(idx) {
}
async function saveCloudModels() {
- await fetch('/api/admin/config', {method:'POST', headers:{'Content-Type':'application/json'},
- body:JSON.stringify({cloud_models: config.cloud_models})});
- toast('Saved');
+ try {
+ var r = await fetch('/api/admin/config', {method:'POST', headers:{'Content-Type':'application/json'},
+ body:JSON.stringify({cloud_models: config.cloud_models})});
+ var d = await r.json();
+ if (d.ok) toast('Cloud models saved', true, (config.cloud_models||[]).length + ' models configured');
+ else toast('Save failed', false);
+ } catch(e) { toast('Save error: ' + e.message, false); }
}
function showAddCloud() { document.getElementById('add-cloud-modal').style.display = ''; }
@@ -2400,12 +2431,17 @@ async function addOR(id, name) {
}
async function saveTimeouts() {
- const g = parseInt(document.getElementById('global-timeout').value) || 300;
+ var g = parseInt(document.getElementById('global-timeout').value) || 300;
config.timeouts = config.timeouts || {};
config.timeouts.global = g;
- await fetch('/api/admin/config', {method:'POST', headers:{'Content-Type':'application/json'},
- body:JSON.stringify({timeouts: config.timeouts})});
- toast('Saved');
+ try {
+ var r = await fetch('/api/admin/config', {method:'POST', headers:{'Content-Type':'application/json'},
+ body:JSON.stringify({timeouts: config.timeouts})});
+ var d = await r.json();
+ var perCount = Object.keys((config.timeouts||{}).per_model||{}).length;
+ if (d.ok) toast('Timeouts saved', true, 'Global: ' + g + 's' + (perCount ? ', ' + perCount + ' overrides' : ''));
+ else toast('Save failed', false);
+ } catch(e) { toast('Save error: ' + e.message, false); }
}
function setModelTimeout(name, val) {
@@ -2478,15 +2514,24 @@ async function removeAllowIP(ip) {
toast('Removed ' + ip);
}
-function toast(msg, ok=true) {
- const t = document.createElement('div');
+function toast(msg, ok=true, detail) {
+ var t = document.createElement('div');
t.className = 'toast ' + (ok ? 'ok' : 'err');
- t.textContent = msg;
+ t.textContent = ok ? '✓ ' + msg : '✗ ' + msg;
+ if (detail) {
+ var d = document.createElement('div');
+ d.className = 'toast-detail';
+ d.textContent = detail;
+ t.appendChild(d);
+ }
document.body.appendChild(t);
- setTimeout(() => t.remove(), 3000);
+ setTimeout(function() { t.style.opacity = '0'; t.style.transition = 'opacity 0.3s'; setTimeout(function() { t.remove(); }, 300); }, 3000);
}
loadConfig();
+
+// Background grid
+!function(){var c=document.getElementById('bg-grid');if(!c)return;var x=c.getContext('2d');function resize(){c.width=window.innerWidth;c.height=window.innerHeight}resize();window.addEventListener('resize',resize);var t=0;function draw(){x.clearRect(0,0,c.width,c.height);var s=50,ox=(t*0.2)%s,oy=(t*0.1)%s;x.fillStyle='rgba(226,181,90,0.025)';for(var gx=-s+ox;gx