feat: persist notification preferences to server-side settings

Notification prefs are now included in the PUT /api/settings payload
and stored in ~/.claudeman/settings.json. On page load, server-stored
prefs are loaded as fallback when localStorage is empty (e.g. new
browser or cleared cache).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
arkon
2026-01-24 02:19:46 +01:00
co-authored by Claude Opus 4.5
parent 1fb1007211
commit f818fbb234
2 changed files with 32 additions and 14 deletions
+31 -13
View File
@@ -388,6 +388,11 @@ class ClaudemanApp {
this.setupEventListeners();
// Start system stats polling
this.startSystemStatsPolling();
// Load server-stored settings (async, re-applies visibility after load)
this.loadAppSettingsFromServer().then(() => {
this.applyHeaderVisibilitySettings();
this.applyMonitorVisibility();
});
}
initTerminal() {
@@ -2518,16 +2523,17 @@ class ClaudemanApp {
localStorage.setItem('claudeman-app-settings', JSON.stringify(settings));
// Save notification preferences separately
const notifPrefsToSave = {
enabled: document.getElementById('appSettingsNotifEnabled').checked,
browserNotifications: document.getElementById('appSettingsNotifBrowser').checked,
audioAlerts: document.getElementById('appSettingsNotifAudio').checked,
stuckThresholdMs: (parseInt(document.getElementById('appSettingsNotifStuckMins').value) || 10) * 60000,
muteCritical: !document.getElementById('appSettingsNotifCritical').checked,
muteWarning: !document.getElementById('appSettingsNotifWarning').checked,
muteInfo: !document.getElementById('appSettingsNotifInfo').checked,
};
if (this.notificationManager) {
this.notificationManager.preferences = {
enabled: document.getElementById('appSettingsNotifEnabled').checked,
browserNotifications: document.getElementById('appSettingsNotifBrowser').checked,
audioAlerts: document.getElementById('appSettingsNotifAudio').checked,
stuckThresholdMs: (parseInt(document.getElementById('appSettingsNotifStuckMins').value) || 10) * 60000,
muteCritical: !document.getElementById('appSettingsNotifCritical').checked,
muteWarning: !document.getElementById('appSettingsNotifWarning').checked,
muteInfo: !document.getElementById('appSettingsNotifInfo').checked,
};
this.notificationManager.preferences = notifPrefsToSave;
this.notificationManager.savePreferences();
}
@@ -2535,12 +2541,12 @@ class ClaudemanApp {
this.applyHeaderVisibilitySettings();
this.applyMonitorVisibility();
// Also save to server
// Save to server (includes notification prefs for cross-browser persistence)
try {
await fetch('/api/settings', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(settings)
body: JSON.stringify({ ...settings, notificationPreferences: notifPrefsToSave })
});
this.showToast('Settings saved', 'success');
} catch (err) {
@@ -2630,10 +2636,22 @@ class ClaudemanApp {
const res = await fetch('/api/settings');
if (res.ok) {
const settings = await res.json();
// Merge with localStorage (server takes precedence)
// Extract notification prefs before merging app settings
const { notificationPreferences, ...appSettings } = settings;
// Merge app settings with localStorage (server takes precedence)
const localSettings = this.loadAppSettingsFromStorage();
const merged = { ...localSettings, ...settings };
const merged = { ...localSettings, ...appSettings };
localStorage.setItem('claudeman-app-settings', JSON.stringify(merged));
// Apply notification prefs from server if present (only if localStorage has none)
if (notificationPreferences && this.notificationManager) {
const localNotifPrefs = localStorage.getItem('claudeman-notification-prefs');
if (!localNotifPrefs) {
this.notificationManager.preferences = notificationPreferences;
this.notificationManager.savePreferences();
}
}
return merged;
}
} catch (err) {
+1 -1
View File
@@ -1306,7 +1306,7 @@ export class WebServer extends EventEmitter {
});
this.app.put('/api/settings', async (req) => {
const settings = req.body as { defaultClaudeMdPath?: string; defaultWorkingDir?: string };
const settings = req.body as Record<string, unknown>;
try {
const dir = dirname(settingsPath);