import { customElement, DeesElement, type TemplateResult, html, property, css, cssManager, state, } from '@design.estate/dees-element'; import { DeesAppuiSecondarymenu, DeesIcon, DeesStatsGrid } from '@design.estate/dees-catalog'; import type { ISecondaryMenuGroup, ISecondaryMenuItem } from '../../elements/interfaces/secondarymenu.js'; import { demo } from './eco-view-system.demo.js'; // Ensure components are registered DeesAppuiSecondarymenu; DeesIcon; DeesStatsGrid; declare global { interface HTMLElementTagNameMap { 'eco-view-system': EcoViewSystem; } } export type TSystemPanel = | 'overview' | 'cpu' | 'memory' | 'storage' | 'network' | 'processes'; @customElement('eco-view-system') export class EcoViewSystem extends DeesElement { public static demo = demo; public static demoGroup = 'Views'; public static styles = [ cssManager.defaultStyles, css` :host { display: block; width: 100%; height: 100%; background: ${cssManager.bdTheme('#f5f5f7', 'hsl(240 6% 10%)')}; color: ${cssManager.bdTheme('hsl(0 0% 10%)', 'hsl(0 0% 98%)')}; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; } .system-container { display: flex; height: 100%; } dees-appui-secondarymenu { flex-shrink: 0; background: ${cssManager.bdTheme('#ffffff', 'hsl(240 6% 8%)')}; border-right: 1px solid ${cssManager.bdTheme('hsl(0 0% 90%)', 'hsl(240 5% 15%)')}; } .content { flex: 1; overflow-y: auto; padding: 32px 48px; } .panel-header { margin-bottom: 32px; } .panel-title { font-size: 28px; font-weight: 600; color: ${cssManager.bdTheme('hsl(0 0% 10%)', 'hsl(0 0% 98%)')}; margin-bottom: 8px; } .panel-description { font-size: 14px; color: ${cssManager.bdTheme('hsl(0 0% 50%)', 'hsl(0 0% 60%)')}; } .stats-section { margin-bottom: 32px; } .section-title { font-size: 13px; font-weight: 600; color: ${cssManager.bdTheme('hsl(0 0% 50%)', 'hsl(0 0% 50%)')}; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 16px; } dees-statsgrid { --dees-statsgrid-gap: 16px; } .process-list { background: ${cssManager.bdTheme('#ffffff', 'hsl(240 6% 12%)')}; border: 1px solid ${cssManager.bdTheme('hsl(0 0% 90%)', 'hsl(240 5% 18%)')}; border-radius: 12px; overflow: hidden; } .process-header { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; padding: 12px 16px; background: ${cssManager.bdTheme('hsl(0 0% 97%)', 'hsl(240 5% 14%)')}; border-bottom: 1px solid ${cssManager.bdTheme('hsl(0 0% 90%)', 'hsl(240 5% 18%)')}; font-size: 12px; font-weight: 600; color: ${cssManager.bdTheme('hsl(0 0% 50%)', 'hsl(0 0% 55%)')}; text-transform: uppercase; letter-spacing: 0.5px; } .process-row { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; padding: 12px 16px; border-bottom: 1px solid ${cssManager.bdTheme('hsl(0 0% 94%)', 'hsl(240 5% 15%)')}; font-size: 14px; color: ${cssManager.bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 85%)')}; } .process-row:last-child { border-bottom: none; } .process-name { font-weight: 500; } .process-value { color: ${cssManager.bdTheme('hsl(0 0% 40%)', 'hsl(0 0% 65%)')}; } .process-value.high { color: hsl(0 84% 60%); font-weight: 500; } `, ]; @property({ type: String }) accessor activePanel: TSystemPanel = 'overview'; // System data (can be set externally) @property({ type: Number }) accessor cpuUsage = 0; @property({ type: Number }) accessor memoryUsage = 0; @property({ type: Number }) accessor diskUsage = 0; @property({ type: Number }) accessor cpuTemp = 0; @property({ type: String }) accessor uptime = '--'; @property({ type: Number }) accessor cpuCores = 0; @property({ type: Number }) accessor cpuPhysicalCores = 0; @property({ type: String }) accessor cpuModel = 'Unknown'; @property({ type: Number }) accessor cpuSpeed = 0; @property({ type: Number }) accessor cpuSpeedMax = 0; @property({ type: Number }) accessor memoryTotal = 0; @property({ type: Number }) accessor memoryUsed = 0; @property({ type: Number }) accessor memoryFree = 0; @property({ type: Number }) accessor memoryAvailable = 0; @property({ type: Number }) accessor memoryCached = 0; @property({ type: Number }) accessor memoryBuffers = 0; @property({ type: Number }) accessor swapTotal = 0; @property({ type: Number }) accessor swapUsed = 0; @property({ type: Number }) accessor diskTotal = 0; @property({ type: Number }) accessor diskUsed = 0; @property({ type: Number }) accessor diskFree = 0; @property({ type: Number }) accessor networkRxSec = 0; @property({ type: Number }) accessor networkTxSec = 0; @property({ type: Number }) accessor networkRxTotal = 0; @property({ type: Number }) accessor networkTxTotal = 0; @property({ type: String }) accessor hostname = 'Unknown'; @property({ type: String }) accessor platform = 'Unknown'; @property({ type: String }) accessor distro = ''; @property({ type: Array }) accessor loadAvg: number[] = [0, 0, 0]; @property({ type: Array }) accessor coreLoads: number[] = []; @state() accessor networkInHistory: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; @state() accessor networkOutHistory: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; @state() accessor memoryUsageHistory: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // Process data @state() accessor processTotal = 0; @state() accessor processRunning = 0; @state() accessor processSleeping = 0; @state() accessor processBlocked = 0; @state() accessor topProcesses: Array<{ name: string; pid: number; cpu: number; memory: number; state: string }> = []; // Public method to update metrics from backend public setMetrics(metrics: { cpu: { usage: number; cores: number; physicalCores?: number; model: string; speed: number; speedMax?: number; loadAvg: number[]; coreLoads?: number[] }; memory: { total: number; used: number; free: number; available?: number; usagePercent: number; swapTotal?: number; swapUsed?: number; cached?: number; buffers?: number }; disk?: { total: number; used: number; free: number; usagePercent: number }; network?: { rxSec: number; txSec: number; rxTotal: number; txTotal: number }; system: { platform: string; distro?: string; hostname: string; uptimeFormatted: string }; }): void { // CPU metrics this.cpuUsage = metrics.cpu.usage; this.cpuCores = metrics.cpu.cores; this.cpuPhysicalCores = metrics.cpu.physicalCores || metrics.cpu.cores; this.cpuModel = metrics.cpu.model; this.cpuSpeed = metrics.cpu.speed; this.cpuSpeedMax = metrics.cpu.speedMax || metrics.cpu.speed; this.loadAvg = metrics.cpu.loadAvg; this.coreLoads = metrics.cpu.coreLoads || []; // Memory metrics this.memoryUsage = metrics.memory.usagePercent; this.memoryTotal = metrics.memory.total; this.memoryUsed = metrics.memory.used; this.memoryFree = metrics.memory.free; this.memoryAvailable = metrics.memory.available || metrics.memory.free; this.memoryCached = metrics.memory.cached || 0; this.memoryBuffers = metrics.memory.buffers || 0; this.swapTotal = metrics.memory.swapTotal || 0; this.swapUsed = metrics.memory.swapUsed || 0; // Update memory usage history for trend chart this.memoryUsageHistory = [...this.memoryUsageHistory.slice(1), metrics.memory.usagePercent]; // Disk metrics if (metrics.disk) { this.diskUsage = metrics.disk.usagePercent; this.diskTotal = metrics.disk.total; this.diskUsed = metrics.disk.used; this.diskFree = metrics.disk.free; } // Network metrics if (metrics.network) { this.networkRxSec = metrics.network.rxSec; this.networkTxSec = metrics.network.txSec; this.networkRxTotal = metrics.network.rxTotal; this.networkTxTotal = metrics.network.txTotal; // Update history for trend charts this.networkInHistory = [...this.networkInHistory.slice(1), metrics.network.rxSec]; this.networkOutHistory = [...this.networkOutHistory.slice(1), metrics.network.txSec]; } // System metrics this.hostname = metrics.system.hostname; this.platform = metrics.system.platform; this.distro = metrics.system.distro || ''; this.uptime = metrics.system.uptimeFormatted; } // Method to set CPU temperature separately (may not always be available) public setTemperature(temp: { main: number; cores?: number[]; max?: number }): void { this.cpuTemp = temp.main || 0; } // Method to set processes data public setProcesses(data: { total: number; running: number; sleeping: number; blocked?: number; list: Array<{ name: string; pid: number; cpu: number; memory: number; state: string }> }): void { this.processTotal = data.total; this.processRunning = data.running; this.processSleeping = data.sleeping; this.processBlocked = data.blocked || 0; this.topProcesses = data.list || []; } // Helper to format bytes private formatBytes(bytes: number): string { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; } private getMenuGroups(): ISecondaryMenuGroup[] { return [ { name: 'Monitor', iconName: 'lucide:activity', items: [ { key: 'overview', iconName: 'lucide:layoutDashboard', action: () => this.activePanel = 'overview', }, ], }, { name: 'Hardware', iconName: 'lucide:cpu', items: [ { key: 'cpu', iconName: 'lucide:cpu', action: () => this.activePanel = 'cpu', }, { key: 'memory', iconName: 'lucide:memoryStick', action: () => this.activePanel = 'memory', }, { key: 'storage', iconName: 'lucide:hardDrive', action: () => this.activePanel = 'storage', }, ], }, { name: 'Network', iconName: 'lucide:network', items: [ { key: 'network', iconName: 'lucide:wifi', action: () => this.activePanel = 'network', }, ], }, { name: 'Software', iconName: 'lucide:layers', items: [ { key: 'processes', iconName: 'lucide:listTree', action: () => this.activePanel = 'processes', }, ], }, ]; } private getSelectedItem(): ISecondaryMenuItem | null { for (const group of this.getMenuGroups()) { for (const item of group.items) { if ('key' in item && item.key === this.activePanel) { return item; } } } return null; } public render(): TemplateResult { return html`
${this.renderActivePanel()}
`; } private renderActivePanel(): TemplateResult { switch (this.activePanel) { case 'overview': return this.renderOverviewPanel(); case 'cpu': return this.renderCpuPanel(); case 'memory': return this.renderMemoryPanel(); case 'storage': return this.renderStoragePanel(); case 'network': return this.renderNetworkPanel(); case 'processes': return this.renderProcessesPanel(); default: return this.renderOverviewPanel(); } } // Helper to format bytes per second as speed private formatSpeed(bytesPerSec: number): string { if (bytesPerSec === 0) return '0 B/s'; const k = 1024; const sizes = ['B/s', 'KB/s', 'MB/s', 'GB/s']; const i = Math.floor(Math.log(bytesPerSec) / Math.log(k)); return parseFloat((bytesPerSec / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; } private renderOverviewPanel(): TemplateResult { const overviewTiles = [ { id: 'cpu', title: 'CPU Usage', value: this.cpuUsage, type: 'gauge' as const, icon: 'lucide:cpu', description: this.cpuModel !== 'Unknown' ? `${this.cpuPhysicalCores} cores @ ${this.cpuSpeed} GHz` : undefined, gaugeOptions: { min: 0, max: 100, thresholds: [ { value: 0, color: 'hsl(142 71% 45%)' }, { value: 60, color: 'hsl(45 93% 47%)' }, { value: 80, color: 'hsl(0 84% 60%)' }, ], }, }, { id: 'memory', title: 'Memory Usage', value: this.memoryUsage, type: 'gauge' as const, icon: 'lucide:memoryStick', description: this.memoryTotal ? `${this.formatBytes(this.memoryUsed)} of ${this.formatBytes(this.memoryTotal)}` : undefined, gaugeOptions: { min: 0, max: 100, thresholds: [ { value: 0, color: 'hsl(142 71% 45%)' }, { value: 70, color: 'hsl(45 93% 47%)' }, { value: 85, color: 'hsl(0 84% 60%)' }, ], }, }, { id: 'disk', title: 'Disk Usage', value: this.diskUsage, type: 'gauge' as const, icon: 'lucide:hardDrive', description: this.diskTotal ? `${this.formatBytes(this.diskUsed)} of ${this.formatBytes(this.diskTotal)}` : undefined, gaugeOptions: { min: 0, max: 100, thresholds: [ { value: 0, color: 'hsl(142 71% 45%)' }, { value: 75, color: 'hsl(45 93% 47%)' }, { value: 90, color: 'hsl(0 84% 60%)' }, ], }, }, { id: 'temp', title: 'CPU Temp', value: this.cpuTemp, unit: '°C', type: 'gauge' as const, icon: 'lucide:thermometer', gaugeOptions: { min: 0, max: 100, thresholds: [ { value: 0, color: 'hsl(217 91% 60%)' }, { value: 50, color: 'hsl(142 71% 45%)' }, { value: 70, color: 'hsl(45 93% 47%)' }, { value: 85, color: 'hsl(0 84% 60%)' }, ], }, }, { id: 'network-in', title: 'Network In', value: this.formatSpeed(this.networkRxSec), type: 'trend' as const, icon: 'lucide:download', trendData: this.networkInHistory, color: 'hsl(142 71% 45%)', description: `Total: ${this.formatBytes(this.networkRxTotal)}`, }, { id: 'network-out', title: 'Network Out', value: this.formatSpeed(this.networkTxSec), type: 'trend' as const, icon: 'lucide:upload', trendData: this.networkOutHistory, color: 'hsl(217 91% 60%)', description: `Total: ${this.formatBytes(this.networkTxTotal)}`, }, { id: 'uptime', title: 'System Uptime', value: this.uptime, type: 'text' as const, icon: 'lucide:clock', color: 'hsl(142 71% 45%)', description: 'Since last reboot', }, { id: 'hostname', title: 'Hostname', value: this.hostname, type: 'text' as const, icon: 'lucide:server', description: this.distro ? `${this.distro} - ${this.cpuCores} threads` : `${this.platform} - ${this.cpuCores} threads`, }, ]; return html`
System Overview
Real-time system performance metrics
`; } private renderCpuPanel(): TemplateResult { // Generate cores data for cpuCores tile type const coresData = this.coreLoads.map((load, index) => ({ id: index, usage: load, label: `Core ${index}`, })); const cpuTiles = [ { id: 'cpu-total', title: 'Total CPU Usage', value: this.cpuUsage, type: 'gauge' as const, icon: 'lucide:cpu', description: `${this.cpuCores} threads on ${this.cpuPhysicalCores} cores`, gaugeOptions: { min: 0, max: 100, thresholds: [ { value: 0, color: 'hsl(142 71% 45%)' }, { value: 60, color: 'hsl(45 93% 47%)' }, { value: 80, color: 'hsl(0 84% 60%)' }, ], }, }, { id: 'cpu-cores', title: 'Core Usage', value: this.cpuUsage, type: 'cpuCores' as const, coresData: coresData, columnSpan: 2, }, { id: 'load-avg', title: 'Load Average', value: this.loadAvg[0]?.toFixed(2) || '0', type: 'trend' as const, icon: 'lucide:activity', trendData: this.loadAvg, description: `1m: ${this.loadAvg[0]?.toFixed(2)}, 5m: ${this.loadAvg[1]?.toFixed(2)}, 15m: ${this.loadAvg[2]?.toFixed(2)}`, }, { id: 'cpu-temp', title: 'Temperature', value: this.cpuTemp, unit: '°C', type: 'gauge' as const, icon: 'lucide:thermometer', gaugeOptions: { min: 0, max: 100, thresholds: [ { value: 0, color: 'hsl(217 91% 60%)' }, { value: 50, color: 'hsl(142 71% 45%)' }, { value: 70, color: 'hsl(45 93% 47%)' }, { value: 85, color: 'hsl(0 84% 60%)' }, ], }, }, { id: 'freq', title: 'Clock Speed', value: this.cpuSpeed.toFixed(1), unit: 'GHz', type: 'number' as const, icon: 'lucide:gauge', description: this.cpuSpeedMax ? `Max: ${this.cpuSpeedMax.toFixed(1)} GHz` : undefined, }, ]; return html`
CPU
${this.cpuModel}
`; } private renderMemoryPanel(): TemplateResult { const swapUsagePercent = this.swapTotal > 0 ? Math.round((this.swapUsed / this.swapTotal) * 100) : 0; const memoryTiles = [ { id: 'ram-usage', title: 'RAM Usage', value: this.memoryUsage, type: 'gauge' as const, icon: 'lucide:memoryStick', gaugeOptions: { min: 0, max: 100, thresholds: [ { value: 0, color: 'hsl(142 71% 45%)' }, { value: 70, color: 'hsl(45 93% 47%)' }, { value: 85, color: 'hsl(0 84% 60%)' }, ], }, description: `${this.formatBytes(this.memoryUsed)} of ${this.formatBytes(this.memoryTotal)}`, }, { id: 'swap-usage', title: 'Swap Usage', value: swapUsagePercent, type: 'gauge' as const, icon: 'lucide:hardDrive', gaugeOptions: { min: 0, max: 100, thresholds: [ { value: 0, color: 'hsl(142 71% 45%)' }, { value: 50, color: 'hsl(45 93% 47%)' }, { value: 75, color: 'hsl(0 84% 60%)' }, ], }, description: this.swapTotal > 0 ? `${this.formatBytes(this.swapUsed)} of ${this.formatBytes(this.swapTotal)}` : 'No swap', }, { id: 'mem-trend', title: 'Memory History', value: `${this.memoryUsage}%`, type: 'trend' as const, icon: 'lucide:trendingUp', trendData: this.memoryUsageHistory, description: 'Recent usage', }, { id: 'cached', title: 'Cached', value: this.formatBytes(this.memoryCached), type: 'text' as const, icon: 'lucide:database', color: 'hsl(217 91% 60%)', }, { id: 'buffers', title: 'Buffers', value: this.formatBytes(this.memoryBuffers), type: 'text' as const, icon: 'lucide:layers', color: 'hsl(262 83% 58%)', }, { id: 'available', title: 'Available', value: this.formatBytes(this.memoryAvailable), type: 'text' as const, icon: 'lucide:checkCircle', color: 'hsl(142 71% 45%)', }, ]; return html`
Memory
RAM and swap usage details
`; } private renderStoragePanel(): TemplateResult { const storageTiles = [ { id: 'disk-total', title: 'Total Storage', value: this.diskUsage, type: 'percentage' as const, icon: 'lucide:hardDrive', description: `${this.formatBytes(this.diskUsed)} of ${this.formatBytes(this.diskTotal)} used`, color: 'hsl(217 91% 60%)', }, { id: 'disk-free', title: 'Free Space', value: this.formatBytes(this.diskFree), type: 'text' as const, icon: 'lucide:hardDrive', description: 'Available storage', color: 'hsl(142 71% 45%)', }, { id: 'disk-used', title: 'Used Space', value: this.formatBytes(this.diskUsed), type: 'text' as const, icon: 'lucide:database', description: 'Currently in use', color: 'hsl(45 93% 47%)', }, { id: 'disk-total-size', title: 'Total Capacity', value: this.formatBytes(this.diskTotal), type: 'text' as const, icon: 'lucide:server', description: 'All filesystems', }, ]; return html`
Storage
Disk usage and I/O performance
`; } private renderNetworkPanel(): TemplateResult { const networkTiles = [ { id: 'download', title: 'Download Speed', value: this.formatSpeed(this.networkRxSec), type: 'trend' as const, icon: 'lucide:download', trendData: this.networkInHistory, color: 'hsl(142 71% 45%)', }, { id: 'upload', title: 'Upload Speed', value: this.formatSpeed(this.networkTxSec), type: 'trend' as const, icon: 'lucide:upload', trendData: this.networkOutHistory, color: 'hsl(217 91% 60%)', }, { id: 'total-down', title: 'Total Downloaded', value: this.formatBytes(this.networkRxTotal), type: 'text' as const, icon: 'lucide:arrowDownCircle', description: 'Since boot', color: 'hsl(142 71% 45%)', }, { id: 'total-up', title: 'Total Uploaded', value: this.formatBytes(this.networkTxTotal), type: 'text' as const, icon: 'lucide:arrowUpCircle', description: 'Since boot', color: 'hsl(217 91% 60%)', }, ]; return html`
Network
Network traffic and connectivity
`; } private renderProcessesPanel(): TemplateResult { const processTiles = [ { id: 'total-processes', title: 'Total Processes', value: this.processTotal, type: 'number' as const, icon: 'lucide:layers', }, { id: 'running', title: 'Running', value: this.processRunning, type: 'number' as const, icon: 'lucide:play', color: 'hsl(142 71% 45%)', }, { id: 'sleeping', title: 'Sleeping', value: this.processSleeping, type: 'number' as const, icon: 'lucide:moon', color: 'hsl(217 91% 60%)', }, { id: 'blocked', title: 'Blocked', value: this.processBlocked, type: 'number' as const, icon: 'lucide:pauseCircle', color: 'hsl(0 84% 60%)', }, ]; return html`
Processes
Running processes and resource usage
Top Processes by CPU
Process PID CPU % Memory %
${this.topProcesses.length > 0 ? this.topProcesses.map(proc => html`
${proc.name} ${proc.pid} ${proc.cpu}% ${proc.memory}%
`) : html`
Loading...
`}
`; } }