feat: add dark mode support v2

This commit is contained in:
2026-03-04 11:31:40 -08:00
parent e05dfef5ab
commit 969a690732
3 changed files with 110 additions and 9 deletions

View File

@@ -1,3 +1,63 @@
// ============ THEME ============
const THEME_STORAGE_KEY = 'agentdash-theme';
const themeToggleBtn = document.getElementById('theme-toggle');
const systemThemeMedia = window.matchMedia('(prefers-color-scheme: dark)');
function getSystemTheme() {
return systemThemeMedia.matches ? 'dark' : 'light';
}
function getSavedTheme() {
try {
const saved = localStorage.getItem(THEME_STORAGE_KEY);
return saved === 'light' || saved === 'dark' ? saved : null;
} catch {
return null;
}
}
function setSavedTheme(theme) {
try {
localStorage.setItem(THEME_STORAGE_KEY, theme);
} catch {
// Ignore localStorage errors (privacy mode, quota, etc.).
}
}
function updateThemeToggleLabel() {
if (!themeToggleBtn) return;
const isDarkTheme = document.documentElement.getAttribute('data-theme') === 'dark';
const nextTheme = isDarkTheme ? 'light' : 'dark';
themeToggleBtn.textContent = isDarkTheme ? 'Light Mode' : 'Dark Mode';
themeToggleBtn.setAttribute('aria-label', `Switch to ${nextTheme} mode`);
}
function applyTheme(theme, { persist = false } = {}) {
document.documentElement.setAttribute('data-theme', theme);
if (persist) setSavedTheme(theme);
updateThemeToggleLabel();
if (usageStats) renderUsageCharts();
}
function initTheme() {
const savedTheme = getSavedTheme();
applyTheme(savedTheme || getSystemTheme());
if (themeToggleBtn) {
themeToggleBtn.addEventListener('click', () => {
const currentTheme = document.documentElement.getAttribute('data-theme') || getSystemTheme();
const nextTheme = currentTheme === 'dark' ? 'light' : 'dark';
applyTheme(nextTheme, { persist: true });
});
}
systemThemeMedia.addEventListener('change', (event) => {
if (!getSavedTheme()) {
applyTheme(event.matches ? 'dark' : 'light');
}
});
}
// ============ NAVIGATION ============
const navLinks = document.querySelectorAll('.nav-link');
const pages = document.querySelectorAll('.page');
@@ -559,6 +619,11 @@ function renderUsageStats() {
}
function renderUsageCharts() {
const rootStyles = getComputedStyle(document.documentElement);
const themeForeground = rootStyles.getPropertyValue('--fg').trim() || '#e0e0e0';
const themeBorder = rootStyles.getPropertyValue('--border').trim() || '#444';
const themePrimary = rootStyles.getPropertyValue('--primary').trim() || '#3498db';
// Provider chart
const providerCtx = document.getElementById('chart-provider').getContext('2d');
@@ -583,7 +648,7 @@ function renderUsageCharts() {
plugins: {
legend: {
position: 'bottom',
labels: { color: '#e0e0e0' }
labels: { color: themeForeground }
}
}
}
@@ -604,7 +669,7 @@ function renderUsageCharts() {
datasets: [{
label: 'Requests',
data: agentData,
backgroundColor: '#3498db'
backgroundColor: themePrimary
}]
},
options: {
@@ -617,12 +682,12 @@ function renderUsageCharts() {
scales: {
y: {
beginAtZero: true,
ticks: { color: '#e0e0e0' },
grid: { color: '#444' }
ticks: { color: themeForeground },
grid: { color: themeBorder }
},
x: {
ticks: { color: '#e0e0e0' },
grid: { color: '#444' }
ticks: { color: themeForeground },
grid: { color: themeBorder }
}
}
}
@@ -708,6 +773,7 @@ function escapeHtml(text) {
// ============ INITIALIZATION ============
document.addEventListener('DOMContentLoaded', () => {
initTheme();
populateAgentDropdown();
loadTasks();