Pulse/frontend-modern/src/utils/localStorage.ts
rcourtman 886368ec44 feat: add sparklines view mode for metrics visualization
Add comprehensive sparkline chart support as an alternative to progress bars
for CPU, Memory, and Disk metrics across all tables.

Features:
- Toggle between bars/trends view modes (persisted to localStorage)
- 30-second sampling with 2-hour retention window using ring buffer
- Canvas-based rendering with shared requestAnimationFrame for efficiency
- Hover tooltips showing exact values and timestamps
- Threshold reference lines (warning/critical) for context
- localStorage persistence survives page refreshes (12-hour max age)
- Dynamic width adaptation to column size
- Namespaced resource IDs prevent collisions
- Lifecycle cleanup prevents memory leaks

Performance optimizations:
- Decoupled sampling from WebSocket handler (6x reduction in recording)
- O(1) ring buffer insertions (no array cloning)
- Batched canvas rendering (single rAF for all sparklines)
- Debounced localStorage writes
- Automatic pruning of removed resources

UI improvements:
- Consistent radio toggle styling matching other filters
- Fixed column widths prevent layout shift during toggle
- Fixed row heights prevent vertical size changes
- Sparklines fill available column width proportionally
2025-11-09 22:31:35 +00:00

111 lines
3 KiB
TypeScript

import { createSignal, createEffect, Signal } from 'solid-js';
/**
* Creates a signal that syncs with localStorage
* @param key - The localStorage key
* @param defaultValue - Default value if nothing in localStorage
* @param parse - Optional parser function for complex types
* @param stringify - Optional stringify function for complex types
*/
function createLocalStorageSignal<T>(
key: string,
defaultValue: T,
parse?: (value: string) => T,
stringify?: (value: T) => string,
): Signal<T> {
// Get initial value from localStorage
const stored = localStorage.getItem(key);
const initialValue =
stored !== null ? (parse ? parse(stored) : (stored as unknown as T)) : defaultValue;
const [value, setValue] = createSignal<T>(initialValue);
// Sync to localStorage on changes
createEffect(() => {
const val = value();
if (val === null || val === undefined) {
localStorage.removeItem(key);
} else {
localStorage.setItem(key, stringify ? stringify(val) : String(val));
}
});
return [value, setValue];
}
/**
* Creates a boolean signal that syncs with localStorage
* @param key - The localStorage key
* @param defaultValue - Default value if nothing in localStorage
*/
export function createLocalStorageBooleanSignal(
key: string,
defaultValue: boolean = false,
): Signal<boolean> {
return createLocalStorageSignal(
key,
defaultValue,
(val) => val === 'true',
(val) => String(val),
);
}
/**
* Creates a number signal that syncs with localStorage
* @param key - The localStorage key
* @param defaultValue - Default value if nothing in localStorage
*/
// Storage keys used throughout the application
export const STORAGE_KEYS = {
// Authentication
AUTH: 'pulse_auth',
LEGACY_TOKEN: 'pulse_api_token',
// UI preferences
DARK_MODE: 'darkMode',
SIDEBAR_COLLAPSED: 'sidebarCollapsed',
// Metadata
GUEST_METADATA: 'pulseGuestMetadata',
DOCKER_METADATA: 'pulseDockerMetadata',
// Platform tracking
PLATFORMS_SEEN: 'pulse-platforms-seen',
// Updates
UPDATES: 'pulse-updates',
// Alert settings
ALERT_HISTORY_TIME_FILTER: 'alertHistoryTimeFilter',
ALERT_HISTORY_SEVERITY_FILTER: 'alertHistorySeverityFilter',
// Storage settings
STORAGE_SHOW_FILTERS: 'storageShowFilters',
STORAGE_VIEW_MODE: 'storageViewMode',
// Backup settings
BACKUPS_SHOW_FILTERS: 'backupsShowFilters',
BACKUPS_USE_RELATIVE_TIME: 'backupsUseRelativeTime',
BACKUPS_SEARCH_HISTORY: 'backupsSearchHistory',
// Dashboard settings
DASHBOARD_SHOW_FILTERS: 'dashboardShowFilters',
DASHBOARD_CARD_VIEW: 'dashboardCardView',
DASHBOARD_AUTO_REFRESH: 'dashboardAutoRefresh',
DASHBOARD_SEARCH_HISTORY: 'dashboardSearchHistory',
// Storage search
STORAGE_SEARCH_HISTORY: 'storageSearchHistory',
// Docker search
DOCKER_SEARCH_HISTORY: 'dockerSearchHistory',
// Hosts search
HOSTS_SEARCH_HISTORY: 'hostsSearchHistory',
// Alerts search
ALERTS_SEARCH_HISTORY: 'alertsSearchHistory',
// Metrics display
METRICS_VIEW_MODE: 'metricsViewMode', // 'bars' | 'sparklines'
} as const;