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
192 lines
7.0 KiB
JavaScript
192 lines
7.0 KiB
JavaScript
// Quick Settings Panel - Centralized configuration for all UI features
|
|
// Accessible via gear icon in header
|
|
|
|
export class QuickSettings {
|
|
constructor(app) {
|
|
this.app = app;
|
|
this.button = null;
|
|
this.panel = null;
|
|
this.isOpen = false;
|
|
}
|
|
|
|
init() {
|
|
this.createButton();
|
|
this.createPanel();
|
|
}
|
|
|
|
createButton() {
|
|
this.button = document.createElement('button');
|
|
this.button.className = 'settings-gear';
|
|
this.button.setAttribute('aria-label', 'Settings');
|
|
this.button.setAttribute('title', 'Quick settings');
|
|
this.button.innerHTML = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>`;
|
|
|
|
this.button.addEventListener('click', () => this.toggle());
|
|
|
|
const headerInfo = document.querySelector('.header-info');
|
|
if (headerInfo) headerInfo.appendChild(this.button);
|
|
}
|
|
|
|
createPanel() {
|
|
this.panel = document.createElement('div');
|
|
this.panel.className = 'quick-settings-panel';
|
|
this.panel.setAttribute('role', 'dialog');
|
|
this.panel.setAttribute('aria-label', 'Quick settings');
|
|
|
|
this.panel.innerHTML = `
|
|
<div class="qs-header">
|
|
<h3>Settings</h3>
|
|
<button class="qs-close" aria-label="Close">×</button>
|
|
</div>
|
|
<div class="qs-body">
|
|
<div class="qs-section">
|
|
<div class="qs-section-title">Display</div>
|
|
<label class="qs-toggle">
|
|
<span>Reduced motion</span>
|
|
<input type="checkbox" id="qs-reduced-motion" ${this.prefersReducedMotion() ? 'checked' : ''}>
|
|
<span class="qs-switch"></span>
|
|
</label>
|
|
<label class="qs-toggle">
|
|
<span>High contrast</span>
|
|
<input type="checkbox" id="qs-high-contrast">
|
|
<span class="qs-switch"></span>
|
|
</label>
|
|
<label class="qs-toggle">
|
|
<span>Compact mode</span>
|
|
<input type="checkbox" id="qs-compact" ${this.getSetting('compact') ? 'checked' : ''}>
|
|
<span class="qs-switch"></span>
|
|
</label>
|
|
</div>
|
|
<div class="qs-section">
|
|
<div class="qs-section-title">Monitoring</div>
|
|
<label class="qs-toggle">
|
|
<span>Health polling</span>
|
|
<input type="checkbox" id="qs-health-polling" checked>
|
|
<span class="qs-switch"></span>
|
|
</label>
|
|
<label class="qs-toggle">
|
|
<span>Auto-reconnect</span>
|
|
<input type="checkbox" id="qs-auto-reconnect" checked>
|
|
<span class="qs-switch"></span>
|
|
</label>
|
|
</div>
|
|
<div class="qs-section">
|
|
<div class="qs-section-title">Data</div>
|
|
<div class="qs-row">
|
|
<span>Clear local data</span>
|
|
<button class="qs-btn-danger" id="qs-clear-data">Clear</button>
|
|
</div>
|
|
<div class="qs-row">
|
|
<span>Reset onboarding</span>
|
|
<button class="qs-btn" id="qs-reset-tour">Reset</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
// Bind events
|
|
this.panel.querySelector('.qs-close').addEventListener('click', () => this.close());
|
|
|
|
this.panel.querySelector('#qs-reduced-motion').addEventListener('change', (e) => {
|
|
document.body.classList.toggle('reduced-motion', e.target.checked);
|
|
this.saveSetting('reduced-motion', e.target.checked);
|
|
});
|
|
|
|
this.panel.querySelector('#qs-high-contrast').addEventListener('change', (e) => {
|
|
document.body.classList.toggle('high-contrast', e.target.checked);
|
|
this.saveSetting('high-contrast', e.target.checked);
|
|
});
|
|
|
|
this.panel.querySelector('#qs-compact').addEventListener('change', (e) => {
|
|
document.body.classList.toggle('compact-mode', e.target.checked);
|
|
this.saveSetting('compact', e.target.checked);
|
|
});
|
|
|
|
this.panel.querySelector('#qs-health-polling').addEventListener('change', (e) => {
|
|
const healthService = this.app?.components?.dashboard?.healthSubscription;
|
|
if (e.target.checked) {
|
|
// Resume would need import - just dispatch event
|
|
document.dispatchEvent(new CustomEvent('health-polling-toggle', { detail: true }));
|
|
} else {
|
|
document.dispatchEvent(new CustomEvent('health-polling-toggle', { detail: false }));
|
|
}
|
|
});
|
|
|
|
this.panel.querySelector('#qs-clear-data').addEventListener('click', () => {
|
|
try {
|
|
localStorage.clear();
|
|
sessionStorage.clear();
|
|
} catch { /* noop */ }
|
|
this.close();
|
|
window.location.reload();
|
|
});
|
|
|
|
this.panel.querySelector('#qs-reset-tour').addEventListener('click', () => {
|
|
try { localStorage.removeItem('ruview-onboarding-done'); } catch { /* noop */ }
|
|
this.close();
|
|
document.dispatchEvent(new CustomEvent('start-onboarding'));
|
|
});
|
|
|
|
document.body.appendChild(this.panel);
|
|
|
|
// Close on outside click
|
|
document.addEventListener('click', (e) => {
|
|
if (this.isOpen && !this.panel.contains(e.target) && !this.button.contains(e.target)) {
|
|
this.close();
|
|
}
|
|
});
|
|
|
|
// Apply saved settings on init
|
|
this.applySavedSettings();
|
|
}
|
|
|
|
applySavedSettings() {
|
|
if (this.getSetting('reduced-motion') || this.prefersReducedMotion()) {
|
|
document.body.classList.add('reduced-motion');
|
|
const cb = this.panel.querySelector('#qs-reduced-motion');
|
|
if (cb) cb.checked = true;
|
|
}
|
|
if (this.getSetting('high-contrast')) {
|
|
document.body.classList.add('high-contrast');
|
|
const cb = this.panel.querySelector('#qs-high-contrast');
|
|
if (cb) cb.checked = true;
|
|
}
|
|
if (this.getSetting('compact')) {
|
|
document.body.classList.add('compact-mode');
|
|
}
|
|
}
|
|
|
|
prefersReducedMotion() {
|
|
return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
}
|
|
|
|
toggle() {
|
|
this.isOpen ? this.close() : this.open();
|
|
}
|
|
|
|
open() {
|
|
this.isOpen = true;
|
|
this.panel.classList.add('open');
|
|
}
|
|
|
|
close() {
|
|
this.isOpen = false;
|
|
this.panel.classList.remove('open');
|
|
}
|
|
|
|
getSetting(key) {
|
|
try { return JSON.parse(localStorage.getItem(`ruview-setting-${key}`)); }
|
|
catch { return null; }
|
|
}
|
|
|
|
saveSetting(key, value) {
|
|
try { localStorage.setItem(`ruview-setting-${key}`, JSON.stringify(value)); }
|
|
catch { /* noop */ }
|
|
}
|
|
|
|
dispose() {
|
|
this.button?.remove();
|
|
this.panel?.remove();
|
|
}
|
|
}
|