9740bc64c9
Continuous Deployment / Deploy to Production (push) Blocked by required conditions
Continuous Deployment / Rollback Deployment (push) Blocked by required conditions
Continuous Deployment / Post-deployment Monitoring (push) Blocked by required conditions
Continuous Deployment / Notify Deployment Status (push) Blocked by required conditions
Firmware QEMU Tests (ADR-061) / QEMU Test (edge-tier1) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (full-adr060) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (tdm-3node) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / Swarm Test (ADR-062) (push) Has been skipped
npm packages / tools/ruview-mcp (node 22) (push) Failing after 1s
nvsim-server → ghcr.io / build-and-publish (push) Failing after 1s
ruview-swarm CI guard / tests (full+train) (push) Failing after 2s
Bench Regression Guard / bench compile-verify (--no-run) (push) Failing after 0s
Bench Regression Guard / bench fast-run (informational, non-gating) (push) Has been skipped
Firmware CI / Verify version.txt matches release tag (push) Has been skipped
Dashboard a11y + cross-browser / a11y (push) Failing after 0s
nvsim Dashboard → GitHub Pages / build-and-deploy (push) Failing after 2s
Firmware CI / Build firmware (esp32s3 / 4mb) (push) Failing after 15s
Firmware QEMU Tests (ADR-061) / Build Espressif QEMU (push) Failing after 1s
Firmware QEMU Tests (ADR-061) / Fuzz Testing (ADR-061 Layer 6) (push) Failing after 1s
Continuous Deployment / Pre-deployment Checks (push) Has been skipped
Continuous Deployment / Deploy to Staging (push) Waiting to run
Firmware CI / Build firmware (esp32c6 / c6-4mb) (push) Failing after 15s
Firmware CI / Build firmware (esp32s3 / 8mb) (push) Failing after 15s
Firmware QEMU Tests (ADR-061) / QEMU Test (boundary-max) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (boundary-min) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (default) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (edge-tier0) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / NVS Matrix Generation (push) Failing after 1s
Security Scanning / Security Policy Compliance (push) Failing after 0s
Security Scanning / Security Report (push) Waiting to run
Security Scanning / Dependency Vulnerability Scan (push) Failing after 0s
Security Scanning / Static Application Security Testing (push) Failing after 1s
Security Scanning / Infrastructure Security Scan (push) Failing after 1s
Security Scanning / Secret Scanning (push) Failing after 1s
npm packages / harness/ruview (node 22) (push) Failing after 17s
Security Scanning / License Compliance Scan (push) Failing after 1s
Security Scanning / Container Security Scan (push) Failing after 4s
three.js demos → GitHub Pages / build-and-deploy (push) Failing after 1s
Verify Pipeline Determinism / Verify Pipeline Determinism (3.11) (push) Failing after 1s
Fix-Marker Regression Guard / Verify fix markers (push) Failing after 1s
ADR-115 MQTT integration tests / mqtt-integration (push) Failing after 1s
npm packages / harness/ruview (node 20) (push) Failing after 1s
npm packages / tools/ruview-mcp (node 20) (push) Failing after 1s
npm packages / tools/ruview-cli (node 20) (push) Failing after 1s
npm packages / tools/ruview-cli (node 22) (push) Failing after 1s
BFLD MQTT Integration / cargo test --features mqtt (live mosquitto) (push) Failing after 29s
ruview-swarm CI guard / build train_marl bin (push) Failing after 2s
ruview-swarm CI guard / clippy (-D warnings, --no-deps) (push) Failing after 3s
ruview-swarm CI guard / tests (ruflo) (push) Failing after 1s
ruview-swarm CI guard / tests (train) (push) Failing after 2s
ruview-swarm CI guard / tests (default) (push) Failing after 2s
Point Cloud Viewer → GitHub Pages / build-and-deploy (push) Failing after 8s
ruview-swarm CI guard / ITAR / publish guard (push) Failing after 0s
wifi-densepose sensing-server → Docker Hub + ghcr.io / build · push · smoke-test (push) Failing after 1s
182 lines
5.5 KiB
JavaScript
182 lines
5.5 KiB
JavaScript
// Activity Log - Scrollable panel showing system events in real-time
|
|
// Toggle with 'L' key or command palette
|
|
|
|
export class ActivityLog {
|
|
constructor() {
|
|
this.panel = null;
|
|
this.visible = false;
|
|
this.entries = [];
|
|
this.maxEntries = 200;
|
|
this.logBody = null;
|
|
this.filters = { info: true, warning: true, error: true, connection: true };
|
|
}
|
|
|
|
init() {
|
|
this.createPanel();
|
|
this.interceptConsole();
|
|
document.addEventListener('toggle-activity-log', () => this.toggle());
|
|
}
|
|
|
|
createPanel() {
|
|
this.panel = document.createElement('div');
|
|
this.panel.className = 'activity-log';
|
|
this.panel.setAttribute('role', 'log');
|
|
this.panel.setAttribute('aria-label', 'Activity log');
|
|
this.panel.innerHTML = `
|
|
<div class="activity-log-header">
|
|
<span class="activity-log-title">Activity Log</span>
|
|
<div class="activity-log-controls">
|
|
<button class="activity-log-filter active" data-filter="info" aria-label="Toggle info messages" title="Info">I</button>
|
|
<button class="activity-log-filter active" data-filter="warning" aria-label="Toggle warnings" title="Warnings">W</button>
|
|
<button class="activity-log-filter active" data-filter="error" aria-label="Toggle errors" title="Errors">E</button>
|
|
<button class="activity-log-filter active" data-filter="connection" aria-label="Toggle connection events" title="Connection">C</button>
|
|
<button class="activity-log-clear" aria-label="Clear log" title="Clear">Clear</button>
|
|
<button class="activity-log-close" aria-label="Close activity log">×</button>
|
|
</div>
|
|
</div>
|
|
<div class="activity-log-body"></div>
|
|
`;
|
|
|
|
this.logBody = this.panel.querySelector('.activity-log-body');
|
|
|
|
// Filter toggles
|
|
this.panel.querySelectorAll('.activity-log-filter').forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
const filter = btn.dataset.filter;
|
|
this.filters[filter] = !this.filters[filter];
|
|
btn.classList.toggle('active', this.filters[filter]);
|
|
this.rerender();
|
|
});
|
|
});
|
|
|
|
// Clear button
|
|
this.panel.querySelector('.activity-log-clear').addEventListener('click', () => {
|
|
this.entries = [];
|
|
this.rerender();
|
|
});
|
|
|
|
// Close button
|
|
this.panel.querySelector('.activity-log-close').addEventListener('click', () => this.hide());
|
|
|
|
// Make resizable by dragging top edge
|
|
this.makeResizable();
|
|
|
|
document.body.appendChild(this.panel);
|
|
}
|
|
|
|
makeResizable() {
|
|
let resizing = false;
|
|
let startY = 0;
|
|
let startHeight = 0;
|
|
|
|
this.panel.addEventListener('mousedown', (e) => {
|
|
// Only top 5px edge
|
|
const rect = this.panel.getBoundingClientRect();
|
|
if (e.clientY - rect.top > 5) return;
|
|
resizing = true;
|
|
startY = e.clientY;
|
|
startHeight = rect.height;
|
|
e.preventDefault();
|
|
});
|
|
|
|
document.addEventListener('mousemove', (e) => {
|
|
if (!resizing) return;
|
|
const delta = startY - e.clientY;
|
|
const newHeight = Math.max(150, Math.min(window.innerHeight * 0.7, startHeight + delta));
|
|
this.panel.style.height = `${newHeight}px`;
|
|
});
|
|
|
|
document.addEventListener('mouseup', () => { resizing = false; });
|
|
}
|
|
|
|
interceptConsole() {
|
|
const origInfo = console.info;
|
|
const origWarn = console.warn;
|
|
const origError = console.error;
|
|
|
|
console.info = (...args) => {
|
|
origInfo.apply(console, args);
|
|
this.addEntry('info', args.map(String).join(' '));
|
|
};
|
|
|
|
console.warn = (...args) => {
|
|
origWarn.apply(console, args);
|
|
const msg = args.map(String).join(' ');
|
|
const type = msg.includes('[WS-') || msg.includes('connect') ? 'connection' : 'warning';
|
|
this.addEntry(type, msg);
|
|
};
|
|
|
|
console.error = (...args) => {
|
|
origError.apply(console, args);
|
|
this.addEntry('error', args.map(String).join(' '));
|
|
};
|
|
}
|
|
|
|
addEntry(type, message) {
|
|
const entry = {
|
|
time: new Date(),
|
|
type,
|
|
message: this.truncate(message, 300)
|
|
};
|
|
|
|
this.entries.push(entry);
|
|
if (this.entries.length > this.maxEntries) {
|
|
this.entries.shift();
|
|
}
|
|
|
|
if (this.visible && this.filters[type]) {
|
|
this.appendEntry(entry);
|
|
// Auto-scroll to bottom
|
|
this.logBody.scrollTop = this.logBody.scrollHeight;
|
|
}
|
|
}
|
|
|
|
appendEntry(entry) {
|
|
const el = document.createElement('div');
|
|
el.className = `activity-log-entry activity-log-${entry.type}`;
|
|
const time = entry.time.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
|
el.innerHTML = `<span class="activity-log-time">${time}</span><span class="activity-log-type">${entry.type.toUpperCase().charAt(0)}</span><span class="activity-log-msg">${this.escapeHtml(entry.message)}</span>`;
|
|
this.logBody.appendChild(el);
|
|
}
|
|
|
|
rerender() {
|
|
this.logBody.innerHTML = '';
|
|
this.entries
|
|
.filter(e => this.filters[e.type])
|
|
.forEach(e => this.appendEntry(e));
|
|
this.logBody.scrollTop = this.logBody.scrollHeight;
|
|
}
|
|
|
|
toggle() {
|
|
this.visible ? this.hide() : this.show();
|
|
}
|
|
|
|
show() {
|
|
this.visible = true;
|
|
this.panel.classList.add('visible');
|
|
this.rerender();
|
|
}
|
|
|
|
hide() {
|
|
this.visible = false;
|
|
this.panel.classList.remove('visible');
|
|
}
|
|
|
|
truncate(str, max) {
|
|
return str.length > max ? str.slice(0, max) + '...' : str;
|
|
}
|
|
|
|
escapeHtml(text) {
|
|
const div = document.createElement('div');
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
|
|
dispose() {
|
|
this.hide();
|
|
if (this.panel?.parentNode) {
|
|
this.panel.parentNode.removeChild(this.panel);
|
|
}
|
|
}
|
|
}
|