import { DeesElement, css, cssManager, customElement, html, property, type TemplateResult, } from '@design.estate/dees-element'; import * as domtools from '@design.estate/dees-domtools'; import { demoFunc } from './dees-chart-log.demo.js'; declare global { interface HTMLElementTagNameMap { 'dees-chart-log': DeesChartLog; } } export interface ILogEntry { timestamp: string; level: 'debug' | 'info' | 'warn' | 'error' | 'success'; message: string; source?: string; } @customElement('dees-chart-log') export class DeesChartLog extends DeesElement { public static demo = demoFunc; @property() public label: string = 'Server Logs'; @property({ type: Array }) public logEntries: ILogEntry[] = []; @property({ type: Boolean }) public autoScroll: boolean = true; @property({ type: Number }) public maxEntries: number = 1000; private logContainer: HTMLDivElement; constructor() { super(); domtools.elementBasic.setup(); } public static styles = [ cssManager.defaultStyles, css` :host { font-family: 'Geist Mono', 'Consolas', 'Monaco', monospace; color: #ccc; font-size: 12px; line-height: 1.4; } .mainbox { position: relative; width: 100%; height: 400px; background: ${cssManager.bdTheme('#f8f9fa', '#0a0a0a')}; border: 1px solid ${cssManager.bdTheme('#dee2e6', '#333')}; border-radius: 8px; display: flex; flex-direction: column; overflow: hidden; } .header { background: ${cssManager.bdTheme('#e9ecef', '#1a1a1a')}; padding: 8px 16px; border-bottom: 1px solid ${cssManager.bdTheme('#dee2e6', '#333')}; display: flex; justify-content: space-between; align-items: center; flex-shrink: 0; } .title { font-weight: 600; color: ${cssManager.bdTheme('#212529', '#fff')}; } .controls { display: flex; gap: 8px; } .control-button { background: ${cssManager.bdTheme('#e9ecef', '#2a2a2a')}; border: 1px solid ${cssManager.bdTheme('#ced4da', '#444')}; border-radius: 4px; padding: 4px 8px; color: ${cssManager.bdTheme('#495057', '#ccc')}; cursor: pointer; font-size: 11px; transition: all 0.2s; } .control-button:hover { background: ${cssManager.bdTheme('#dee2e6', '#3a3a3a')}; border-color: ${cssManager.bdTheme('#adb5bd', '#555')}; } .control-button.active { background: ${cssManager.bdTheme('#007bff', '#4a4a4a')}; color: ${cssManager.bdTheme('#fff', '#fff')}; } .logContainer { flex: 1; overflow-y: auto; overflow-x: hidden; padding: 8px 16px; font-size: 12px; } .logEntry { margin-bottom: 2px; display: flex; white-space: pre-wrap; word-break: break-all; } .timestamp { color: ${cssManager.bdTheme('#6c757d', '#666')}; margin-right: 8px; flex-shrink: 0; } .level { margin-right: 8px; padding: 0 6px; border-radius: 3px; font-weight: 600; text-transform: uppercase; font-size: 10px; flex-shrink: 0; } .level.debug { color: ${cssManager.bdTheme('#6c757d', '#999')}; background: ${cssManager.bdTheme('rgba(108, 117, 125, 0.1)', '#333')}; } .level.info { color: ${cssManager.bdTheme('#0066cc', '#4a9eff')}; background: ${cssManager.bdTheme('rgba(0, 102, 204, 0.1)', 'rgba(74, 158, 255, 0.1)')}; } .level.warn { color: ${cssManager.bdTheme('#ff8800', '#ffb84a')}; background: ${cssManager.bdTheme('rgba(255, 136, 0, 0.1)', 'rgba(255, 184, 74, 0.1)')}; } .level.error { color: ${cssManager.bdTheme('#dc3545', '#ff4a4a')}; background: ${cssManager.bdTheme('rgba(220, 53, 69, 0.1)', 'rgba(255, 74, 74, 0.1)')}; } .level.success { color: ${cssManager.bdTheme('#28a745', '#4aff88')}; background: ${cssManager.bdTheme('rgba(40, 167, 69, 0.1)', 'rgba(74, 255, 136, 0.1)')}; } .source { color: ${cssManager.bdTheme('#6c757d', '#888')}; margin-right: 8px; flex-shrink: 0; } .message { color: ${cssManager.bdTheme('#212529', '#ddd')}; flex: 1; } .empty-state { display: flex; align-items: center; justify-content: center; height: 100%; color: ${cssManager.bdTheme('#6c757d', '#666')}; font-style: italic; } /* Custom scrollbar */ .logContainer::-webkit-scrollbar { width: 8px; } .logContainer::-webkit-scrollbar-track { background: ${cssManager.bdTheme('#e9ecef', '#1a1a1a')}; } .logContainer::-webkit-scrollbar-thumb { background: ${cssManager.bdTheme('#adb5bd', '#444')}; border-radius: 4px; } .logContainer::-webkit-scrollbar-thumb:hover { background: ${cssManager.bdTheme('#6c757d', '#555')}; } `, ]; public render(): TemplateResult { return html`
${this.label}
${this.logEntries.length === 0 ? html`
No logs to display
` : this.logEntries.map(entry => this.renderLogEntry(entry)) }
`; } private renderLogEntry(entry: ILogEntry): TemplateResult { const timestamp = new Date(entry.timestamp).toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit', fractionalSecondDigits: 3 }); return html`
${timestamp} ${entry.level} ${entry.source ? html`[${entry.source}]` : ''} ${entry.message}
`; } public async firstUpdated() { await this.domtoolsPromise; this.logContainer = this.shadowRoot.querySelector('.logContainer'); // Initialize with demo server logs const demoLogs: ILogEntry[] = [ { timestamp: new Date().toISOString(), level: 'info', message: 'Server started on port 3000', source: 'Server' }, { timestamp: new Date().toISOString(), level: 'debug', message: 'Loading configuration from /etc/app/config.json', source: 'Config' }, { timestamp: new Date().toISOString(), level: 'info', message: 'Connected to MongoDB at mongodb://localhost:27017', source: 'Database' }, { timestamp: new Date().toISOString(), level: 'success', message: 'Database connection established successfully', source: 'Database' }, { timestamp: new Date().toISOString(), level: 'warn', message: 'No SSL certificate found, using self-signed certificate', source: 'Security' }, { timestamp: new Date().toISOString(), level: 'info', message: 'API routes initialized: GET /api/users, POST /api/users, DELETE /api/users/:id', source: 'Router' }, { timestamp: new Date().toISOString(), level: 'debug', message: 'Middleware stack: cors, bodyParser, authentication, errorHandler', source: 'Middleware' }, { timestamp: new Date().toISOString(), level: 'info', message: 'WebSocket server listening on ws://localhost:3001', source: 'WebSocket' }, ]; this.logEntries = demoLogs; this.scrollToBottom(); } public async updateLog(entries?: ILogEntry[]) { if (entries) { // Add new entries this.logEntries = [...this.logEntries, ...entries]; // Trim if exceeds max entries if (this.logEntries.length > this.maxEntries) { this.logEntries = this.logEntries.slice(-this.maxEntries); } // Trigger re-render this.requestUpdate(); // Auto-scroll if enabled await this.updateComplete; if (this.autoScroll) { this.scrollToBottom(); } } } public clearLogs() { this.logEntries = []; this.requestUpdate(); } private scrollToBottom() { if (this.logContainer) { this.logContainer.scrollTop = this.logContainer.scrollHeight; } } public addLog(level: ILogEntry['level'], message: string, source?: string) { const newEntry: ILogEntry = { timestamp: new Date().toISOString(), level, message, source }; this.updateLog([newEntry]); } }