128 lines
3.0 KiB
TypeScript
128 lines
3.0 KiB
TypeScript
/**
|
|
* EcoOS Logs View
|
|
* Tabbed log viewer for daemon and system logs with auto-scroll
|
|
*/
|
|
|
|
import {
|
|
html,
|
|
DeesElement,
|
|
customElement,
|
|
property,
|
|
state,
|
|
css,
|
|
type TemplateResult,
|
|
} from '@design.estate/dees-element';
|
|
|
|
import { sharedStyles } from '../styles/shared.js';
|
|
|
|
@customElement('ecoos-logs')
|
|
export class EcoosLogs extends DeesElement {
|
|
@property({ type: Array })
|
|
public accessor daemonLogs: string[] = [];
|
|
|
|
@property({ type: Array })
|
|
public accessor systemLogs: string[] = [];
|
|
|
|
@state()
|
|
private accessor activeTab: 'daemon' | 'system' = 'daemon';
|
|
|
|
public static styles = [
|
|
sharedStyles,
|
|
css`
|
|
:host {
|
|
display: block;
|
|
padding: 20px;
|
|
}
|
|
|
|
.logs-container {
|
|
background: var(--ecoos-card);
|
|
border: 1px solid var(--ecoos-border);
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.logs-header {
|
|
padding: 12px 16px;
|
|
border-bottom: 1px solid var(--ecoos-border);
|
|
}
|
|
|
|
.logs-content {
|
|
height: 500px;
|
|
overflow-y: auto;
|
|
font-family: 'SF Mono', Monaco, monospace;
|
|
font-size: 12px;
|
|
line-height: 1.6;
|
|
background: #0d0d0d;
|
|
padding: 12px;
|
|
}
|
|
|
|
.log-entry {
|
|
white-space: pre-wrap;
|
|
word-break: break-all;
|
|
padding: 2px 0;
|
|
}
|
|
|
|
.empty-message {
|
|
color: var(--ecoos-text-dim);
|
|
padding: 20px;
|
|
text-align: center;
|
|
}
|
|
`,
|
|
];
|
|
|
|
render(): TemplateResult {
|
|
const logs = this.activeTab === 'daemon' ? this.daemonLogs : this.systemLogs;
|
|
|
|
return html`
|
|
<div class="logs-container">
|
|
<div class="logs-header">
|
|
<div class="tabs">
|
|
<div
|
|
class="tab ${this.activeTab === 'daemon' ? 'active' : ''}"
|
|
@click=${() => this.switchTab('daemon')}
|
|
>
|
|
Daemon Logs
|
|
</div>
|
|
<div
|
|
class="tab ${this.activeTab === 'system' ? 'active' : ''}"
|
|
@click=${() => this.switchTab('system')}
|
|
>
|
|
System Logs
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="logs-content" id="logs-content">
|
|
${logs.length === 0
|
|
? html`<div class="empty-message">No logs available</div>`
|
|
: logs.map(log => html`<div class="log-entry">${log}</div>`)
|
|
}
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
private switchTab(tab: 'daemon' | 'system'): void {
|
|
this.activeTab = tab;
|
|
this.scrollToBottom();
|
|
}
|
|
|
|
updated(changedProperties: Map<string, unknown>): void {
|
|
super.updated(changedProperties);
|
|
|
|
// Auto-scroll when logs change
|
|
if (changedProperties.has('daemonLogs') || changedProperties.has('systemLogs')) {
|
|
this.scrollToBottom();
|
|
}
|
|
}
|
|
|
|
private scrollToBottom(): void {
|
|
requestAnimationFrame(() => {
|
|
const content = this.shadowRoot?.getElementById('logs-content');
|
|
if (content) {
|
|
content.scrollTop = content.scrollHeight;
|
|
}
|
|
});
|
|
}
|
|
}
|