a0dd516cd5
Systematic cleanup of every error handling anti-pattern detected by the automated scanner. 289 issues fixed via code changes, 12 approved with specific technical justifications. Changes across 90 files: - GENERIC_CATCH (141): Added instanceof Error type discrimination - LARGE_TRY_BLOCK (82): Extracted helper methods to narrow try scope to ≤10 lines - NO_LOGGING_IN_CATCH (65): Added logger/console calls for error visibility - CATCH_AND_CONTINUE_CRITICAL_PATH (10): Added throw/return or approved overrides - ERROR_STRING_MATCHING (2): Approved with rationale (no typed error classes) - ERROR_MESSAGE_GUESSING (1): Replaced chained .includes() with documented pattern array - PROMISE_CATCH_NO_LOGGING (1): Added logging to .catch() handler Also fixes a detector bug where nested try/catch inside a catch block corrupted brace-depth tracking, causing false positives. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
77 lines
2.4 KiB
TypeScript
77 lines
2.4 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
|
|
export type ThemePreference = 'system' | 'light' | 'dark';
|
|
export type ResolvedTheme = 'light' | 'dark';
|
|
|
|
const STORAGE_KEY = 'claude-mem-theme';
|
|
|
|
function getSystemTheme(): ResolvedTheme {
|
|
if (typeof window === 'undefined') return 'dark';
|
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
}
|
|
|
|
function getStoredPreference(): ThemePreference {
|
|
try {
|
|
const stored = localStorage.getItem(STORAGE_KEY);
|
|
if (stored === 'system' || stored === 'light' || stored === 'dark') {
|
|
return stored;
|
|
}
|
|
} catch (e: unknown) {
|
|
console.warn('Failed to read theme preference from localStorage:', e instanceof Error ? e.message : String(e));
|
|
}
|
|
return 'system';
|
|
}
|
|
|
|
function resolveTheme(preference: ThemePreference): ResolvedTheme {
|
|
if (preference === 'system') {
|
|
return getSystemTheme();
|
|
}
|
|
return preference;
|
|
}
|
|
|
|
export function useTheme() {
|
|
const [preference, setPreference] = useState<ThemePreference>(getStoredPreference);
|
|
const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>(() =>
|
|
resolveTheme(getStoredPreference())
|
|
);
|
|
|
|
// Update resolved theme when preference changes
|
|
useEffect(() => {
|
|
const newResolvedTheme = resolveTheme(preference);
|
|
setResolvedTheme(newResolvedTheme);
|
|
document.documentElement.setAttribute('data-theme', newResolvedTheme);
|
|
}, [preference]);
|
|
|
|
// Listen for system theme changes when preference is 'system'
|
|
useEffect(() => {
|
|
if (preference !== 'system') return;
|
|
|
|
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
const handleChange = (e: MediaQueryListEvent) => {
|
|
const newTheme = e.matches ? 'dark' : 'light';
|
|
setResolvedTheme(newTheme);
|
|
document.documentElement.setAttribute('data-theme', newTheme);
|
|
};
|
|
|
|
mediaQuery.addEventListener('change', handleChange);
|
|
return () => mediaQuery.removeEventListener('change', handleChange);
|
|
}, [preference]);
|
|
|
|
const setThemePreference = (newPreference: ThemePreference) => {
|
|
try {
|
|
localStorage.setItem(STORAGE_KEY, newPreference);
|
|
setPreference(newPreference);
|
|
} catch (e: unknown) {
|
|
console.warn('Failed to save theme preference to localStorage:', e instanceof Error ? e.message : String(e));
|
|
// Still update the theme even if localStorage fails
|
|
setPreference(newPreference);
|
|
}
|
|
};
|
|
|
|
return {
|
|
preference,
|
|
resolvedTheme,
|
|
setThemePreference
|
|
};
|
|
}
|