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
111 lines
3 KiB
TypeScript
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;
|