Changeset 0.15.0 (#71)

This commit is contained in:
2026-02-26 21:19:55 +00:00
parent e2149e249d
commit 2d79ff593b
35 changed files with 3218 additions and 504 deletions

View File

@@ -58,13 +58,36 @@ function updateAvatarPreview(dataURI) {
// ── Settings Handlers ────────────────────────
function handleSaveSettings() {
async function handleSaveSettings() {
App.settings.model = document.getElementById('settingsModel').value || App.settings.model;
App.settings.systemPrompt = document.getElementById('settingsSystemPrompt').value.trim();
App.settings.maxTokens = parseInt(document.getElementById('settingsMaxTokens').value) || 0; // 0 = auto
App.settings.temperature = parseFloat(document.getElementById('settingsTemp').value) || 0.7;
App.settings.showThinking = document.getElementById('settingsThinking').checked;
saveSettings();
// ── Save profile fields (display name, email) ──
const displayName = document.getElementById('profileDisplayName').value.trim();
const email = document.getElementById('profileEmail').value.trim();
const profileUpdates = {};
if (displayName !== (API.user?.display_name || '')) profileUpdates.display_name = displayName;
if (email !== (API.user?.email || '')) profileUpdates.email = email;
if (Object.keys(profileUpdates).length > 0) {
try {
const updated = await API.updateProfile(profileUpdates);
// Sync local user object so sidebar/avatar reflect the change
if (updated) {
if (updated.display_name !== undefined) API.user.display_name = updated.display_name;
if (updated.email !== undefined) API.user.email = updated.email;
API.saveTokens();
UI.updateUser();
}
} catch (e) {
console.warn('Profile save failed:', e.message);
UI.toast('Profile update failed: ' + e.message, 'error');
}
}
UI.updateModelSelector();
UI.updateCapabilityBadges();
UI.closeSettings();
@@ -204,6 +227,20 @@ async function handleSaveAdminSettings() {
};
await API.adminUpdateSetting('search_config', { value: searchConfig });
// Auto-Compaction config → global_settings
const compactionEnabled = document.getElementById('adminCompactionEnabled')?.checked || false;
const compactionThreshold = parseInt(document.getElementById('adminCompactionThreshold')?.value) || 70;
const compactionCooldown = parseInt(document.getElementById('adminCompactionCooldown')?.value) || 30;
await API.adminUpdateSetting('auto_compaction', { value: {
enabled: compactionEnabled,
threshold: compactionThreshold,
cooldown: compactionCooldown,
}});
// Scanner reads these individual keys each tick
await API.adminUpdateSetting('auto_compaction_enabled', { value: compactionEnabled });
await API.adminUpdateSetting('auto_compaction_threshold', { value: compactionThreshold / 100 });
await API.adminUpdateSetting('auto_compaction_cooldown_minutes', { value: compactionCooldown });
UI.toast('Settings saved', 'success');
// Live-apply: refresh policies and dependent UI
await initBanners();
@@ -233,6 +270,9 @@ function _initUserRolePrimitive() {
return list.filter(c => c.scope === 'personal');
},
fetchModels: async () => {
// Refresh App.models to ensure personal provider models are current.
// Without this, newly added providers may not appear in the dropdown.
await fetchModels();
return App.models || [];
},
fetchRoles: async () => {
@@ -591,6 +631,11 @@ function _initSettingsListeners() {
document.getElementById('adminBannerBgHex')?.addEventListener('input', (e) => { if (/^#[0-9a-f]{6}$/i.test(e.target.value)) { document.getElementById('adminBannerBg').value = e.target.value; UI.updateBannerPreview(); }});
document.getElementById('adminBannerFgHex')?.addEventListener('input', (e) => { if (/^#[0-9a-f]{6}$/i.test(e.target.value)) { document.getElementById('adminBannerFg').value = e.target.value; UI.updateBannerPreview(); }});
// Admin — auto-compaction controls
document.getElementById('adminCompactionEnabled')?.addEventListener('change', (e) => {
document.getElementById('compactionConfigFields').style.display = e.target.checked ? '' : 'none';
});
// Admin — search provider controls
document.getElementById('adminSearchProvider')?.addEventListener('change', (e) => {
document.getElementById('searxngConfigFields').style.display = e.target.value === 'searxng' ? '' : 'none';