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(); this.setupEventListeners();
// Start system stats polling // Start system stats polling
this.startSystemStatsPolling(); this.startSystemStatsPolling();
// Load server-stored settings (async, re-applies visibility after load)
this.loadAppSettingsFromServer().then(() => {
this.applyHeaderVisibilitySettings();
this.applyMonitorVisibility();
});
} }
initTerminal() { initTerminal() {
@@ -2518,16 +2523,17 @@ class ClaudemanApp {
localStorage.setItem('claudeman-app-settings', JSON.stringify(settings)); localStorage.setItem('claudeman-app-settings', JSON.stringify(settings));
// Save notification preferences separately // 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) { if (this.notificationManager) {
this.notificationManager.preferences = { this.notificationManager.preferences = 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,
};
this.notificationManager.savePreferences(); this.notificationManager.savePreferences();
} }
@@ -2535,12 +2541,12 @@ class ClaudemanApp {
this.applyHeaderVisibilitySettings(); this.applyHeaderVisibilitySettings();
this.applyMonitorVisibility(); this.applyMonitorVisibility();
// Also save to server // Save to server (includes notification prefs for cross-browser persistence)
try { try {
await fetch('/api/settings', { await fetch('/api/settings', {
method: 'PUT', method: 'PUT',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(settings) body: JSON.stringify({ ...settings, notificationPreferences: notifPrefsToSave })
}); });
this.showToast('Settings saved', 'success'); this.showToast('Settings saved', 'success');
} catch (err) { } catch (err) {
@@ -2630,10 +2636,22 @@ class ClaudemanApp {
const res = await fetch('/api/settings'); const res = await fetch('/api/settings');
if (res.ok) { if (res.ok) {
const settings = await res.json(); 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 localSettings = this.loadAppSettingsFromStorage();
const merged = { ...localSettings, ...settings }; const merged = { ...localSettings, ...appSettings };
localStorage.setItem('claudeman-app-settings', JSON.stringify(merged)); 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; return merged;
} }
} catch (err) { } catch (err) {
+1 -1
View File
@@ -1306,7 +1306,7 @@ export class WebServer extends EventEmitter {
}); });
this.app.put('/api/settings', async (req) => { 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 { try {
const dir = dirname(settingsPath); const dir = dirname(settingsPath);