Changeset 0.22.10 (#152)

This commit is contained in:
2026-03-04 22:37:57 +00:00
parent 6ed76fb2d3
commit 40d9834f64
15 changed files with 1014 additions and 515 deletions

View File

@@ -141,7 +141,7 @@ Object.assign(UI, {
const prefs = JSON.parse(localStorage.getItem('cs-appearance') || '{}');
const scale = prefs.scale || 100;
const msgFont = prefs.msgFont || 14;
const theme = prefs.theme || 'dark';
const theme = prefs.theme || 'system';
const keymap = prefs.editorKeymap || 'standard';
const scaleEl = document.getElementById('settingsScale');
@@ -164,7 +164,7 @@ Object.assign(UI, {
// Load saved prefs on startup
const prefs = JSON.parse(localStorage.getItem('cs-appearance') || '{}');
UI.applyAppearance(prefs.scale || 100, prefs.msgFont || 14);
UI.applyTheme(prefs.theme || 'dark');
UI.applyTheme(prefs.theme || 'system');
// Live preview on slider change
const scaleEl = document.getElementById('settingsScale');
@@ -380,21 +380,27 @@ Object.assign(UI, {
try {
const resp = await API.teamListMembers(teamId);
const members = resp.data || [];
el.innerHTML = members.map(m => `
<div class="admin-user-row" style="padding:6px 0">
<div class="admin-user-info">
<strong style="font-size:13px">${esc(m.display_name || m.email)}</strong>
<span class="badge-${m.role === 'admin' ? 'admin' : 'user'}" style="font-size:10px">${m.role}</span>
</div>
<div class="admin-user-actions">
<select onchange="settingsUpdateTeamMember('${teamId}','${m.id}',this.value)" class="inline-select">
<option value="member" ${m.role === 'member' ? 'selected' : ''}>Member</option>
<option value="admin" ${m.role === 'admin' ? 'selected' : ''}>Admin</option>
</select>
<button class="btn-delete" onclick="settingsRemoveTeamMember('${teamId}','${m.id}','${esc(m.email)}')" title="Remove">✕</button>
</div>
</div>
`).join('') || '<div class="empty-hint">No members</div>';
if (!members.length) { el.innerHTML = '<div class="empty-hint">No members</div>'; return; }
el.innerHTML = `
<table class="admin-table">
<thead><tr><th>Member</th><th>Role</th><th></th></tr></thead>
<tbody>${members.map(m => `<tr>
<td>
<div style="font-weight:500;font-size:13px">${esc(m.display_name || m.email)}</div>
${m.user_role === 'admin' ? '<span class="badge-admin" style="font-size:9px">sys-admin</span>' : ''}
</td>
<td>
<select onchange="settingsUpdateTeamMember('${teamId}','${m.id}',this.value)" class="inline-select" style="font-size:12px;padding:3px 6px;">
<option value="member" ${m.role === 'member' ? 'selected' : ''}>Member</option>
<option value="admin" ${m.role === 'admin' ? 'selected' : ''}>Admin</option>
</select>
</td>
<td class="admin-actions-cell">
<button class="btn-icon btn-icon-danger" onclick="settingsRemoveTeamMember('${teamId}','${m.id}','${esc(m.email)}')" title="Remove">🗑</button>
</td>
</tr>`).join('')}</tbody>
</table>
<div class="admin-table-footer"><span>${members.length} member${members.length !== 1 ? 's' : ''}</span></div>`;
} catch (e) { el.innerHTML = `<div class="error-hint">${esc(e.message)}</div>`; }
},
@@ -515,17 +521,45 @@ Object.assign(UI, {
try {
const resp = await API.teamListGroups(teamId);
const groups = resp.data || [];
el.innerHTML = groups.map(g => `
<div class="admin-user-row" style="padding:6px 0">
<div class="admin-user-info">
<div><strong>${esc(g.name)}</strong></div>
<div class="text-muted" style="font-size:11px">${esc(g.description || 'No description')} · ${g.member_count} member${g.member_count !== 1 ? 's' : ''}</div>
</div>
</div>
`).join('') || '<div class="empty-hint">No groups assigned to this team yet</div>';
if (!groups.length) { el.innerHTML = '<div class="empty-hint">No groups assigned to this team yet</div>'; return; }
el.innerHTML = `
<table class="admin-table">
<thead><tr><th>Group</th><th>Members</th></tr></thead>
<tbody>${groups.map(g => `<tr>
<td><div style="font-weight:500">${esc(g.name)}</div><div class="admin-user-handle">${esc(g.description || 'No description')}</div></td>
<td style="font-size:12px;color:var(--text-2)">${g.member_count} member${g.member_count !== 1 ? 's' : ''}</td>
</tr>`).join('')}</tbody>
</table>`;
} catch (e) { el.innerHTML = `<div class="error-hint">${esc(e.message)}</div>`; }
},
async loadTeamManageSettings(teamId) {
if (!teamId) return;
try {
const team = await API.teamGetTeam ? API.teamGetTeam(teamId) : await API.adminGetTeam(teamId);
const settings = typeof team.settings === 'string' ? JSON.parse(team.settings || '{}') : (team.settings || {});
const privEl = document.getElementById('teamSettingPrivatePolicy');
const allowEl = document.getElementById('teamSettingAllowProviders');
if (privEl) privEl.checked = !!settings.require_private_providers;
if (allowEl) allowEl.checked = settings.allow_team_providers !== false;
// Wire save button (once)
const saveBtn = document.getElementById('teamSettingSaveBtn');
if (saveBtn && !saveBtn._wired) {
saveBtn._wired = true;
saveBtn.addEventListener('click', async () => {
const tid = UI._managingTeamId;
const priv = document.getElementById('teamSettingPrivatePolicy')?.checked || false;
const allow = document.getElementById('teamSettingAllowProviders')?.checked !== false;
try {
await API.adminUpdateTeam(tid, { settings: JSON.stringify({ require_private_providers: priv, allow_team_providers: allow }) });
UI.toast('Team settings saved', 'success');
} catch (e) { UI.toast(e.message, 'error'); }
});
}
} catch (e) { /* proceed with defaults */ }
},
async loadMyUsage() {
const el = document.getElementById('myUsageTotals')?.parentElement;
if (!el) return;