MQL5-Google-Onedrive/sw-inspector.html
google-labs-jules[bot] a41c83d9b6 🎨 Palette: Improve color contrast and accessibility
- Replaced #667eea with #4f46e5 to meet WCAG AA contrast standards.
- Updated hover color to #4338ca.
- Added aria-label to Service Worker update button.
- Updated manifest.json theme color.
2026-02-13 11:40:16 +00:00

457 lines
15 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Service Worker Inspector - MQL5 Trading Automation</title>
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#4f46e5">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #4f46e5 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1000px;
margin: 0 auto;
}
.header {
background: white;
padding: 30px;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
margin-bottom: 30px;
text-align: center;
}
.header h1 {
color: #333;
font-size: 2em;
margin-bottom: 10px;
}
.header p {
color: #666;
font-size: 1em;
}
.card {
background: white;
padding: 25px;
border-radius: 15px;
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.card h2 {
color: #4f46e5;
margin-bottom: 15px;
font-size: 1.5em;
}
.info-grid {
display: grid;
grid-template-columns: 200px 1fr;
gap: 15px;
margin-bottom: 15px;
}
.info-label {
color: #666;
font-weight: 600;
}
.info-value {
color: #333;
word-break: break-all;
}
.status-badge {
display: inline-block;
padding: 5px 15px;
border-radius: 20px;
font-size: 0.9em;
font-weight: 600;
}
.status-active {
background: #10b981;
color: white;
}
.status-inactive {
background: #ef4444;
color: white;
}
.status-installing {
background: #f59e0b;
color: white;
}
.btn {
display: inline-block;
padding: 12px 24px;
background: #4f46e5;
color: white;
border: none;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
transition: background 0.3s;
margin-right: 10px;
margin-bottom: 10px;
}
.btn:hover {
background: #4338ca;
}
.btn-danger {
background: #ef4444;
}
.btn-danger:hover {
background: #dc2626;
}
.log-container {
background: #1a1a1a;
color: #0f0;
padding: 15px;
border-radius: 8px;
font-family: 'Courier New', monospace;
font-size: 0.9em;
max-height: 300px;
overflow-y: auto;
margin-top: 15px;
}
.log-entry {
margin-bottom: 5px;
}
.log-timestamp {
color: #888;
}
.cache-list {
list-style: none;
}
.cache-item {
padding: 10px;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
.cache-item:last-child {
border-bottom: none;
}
.cache-name {
font-weight: 600;
color: #333;
}
.cache-size {
color: #4f46e5;
font-weight: 600;
}
.back-link {
display: inline-block;
color: white;
text-decoration: none;
margin-bottom: 20px;
padding: 10px 20px;
background: rgba(255, 255, 255, 0.2);
border-radius: 8px;
transition: background 0.3s;
}
.back-link:hover {
background: rgba(255, 255, 255, 0.3);
}
</style>
</head>
<body>
<div class="container">
<a href="/" class="back-link">← Back to Dashboard</a>
<div class="header">
<h1><span role="img" aria-label="Magnifying Glass">🔍</span> Service Worker Inspector</h1>
<p>Monitor and manage your Progressive Web App</p>
</div>
<div class="card">
<h2>Service Worker Status</h2>
<div class="info-grid">
<div class="info-label">Status:</div>
<div class="info-value">
<span id="sw-status" class="status-badge status-inactive">Checking...</span>
</div>
<div class="info-label">State:</div>
<div class="info-value" id="sw-state">-</div>
<div class="info-label">Scope:</div>
<div class="info-value" id="sw-scope">-</div>
<div class="info-label">Script URL:</div>
<div class="info-value" id="sw-url">-</div>
<div class="info-label">Update Available:</div>
<div class="info-value" id="sw-update">-</div>
</div>
<div style="margin-top: 20px;">
<button class="btn" onclick="checkUpdate()">Check for Updates</button>
<button class="btn" onclick="forceUpdate()">Force Update</button>
<button class="btn btn-danger" onclick="unregisterSW()">Unregister</button>
</div>
</div>
<div class="card">
<h2>Cache Storage</h2>
<div id="cache-info">
<p>Loading cache information...</p>
</div>
<div style="margin-top: 20px;">
<button class="btn" onclick="refreshCacheInfo()">Refresh Cache Info</button>
<button class="btn btn-danger" onclick="clearAllCaches()">Clear All Caches</button>
</div>
</div>
<div class="card">
<h2>Activity Log</h2>
<div class="log-container" id="log-container">
<div class="log-entry">
<span class="log-timestamp">[Starting]</span> Service Worker Inspector initialized
</div>
</div>
<div style="margin-top: 15px;">
<button class="btn" onclick="clearLog()">Clear Log</button>
</div>
</div>
<div class="card">
<h2>PWA Installation</h2>
<div class="info-grid">
<div class="info-label">Installable:</div>
<div class="info-value" id="pwa-installable">Checking...</div>
<div class="info-label">Display Mode:</div>
<div class="info-value" id="display-mode">-</div>
</div>
<div style="margin-top: 20px;">
<button class="btn" id="install-btn" style="display: none;" onclick="installPWA()">Install App</button>
</div>
</div>
</div>
<script>
let deferredPrompt;
const logs = [];
function log(message) {
const timestamp = new Date().toLocaleTimeString();
logs.push({ timestamp, message });
const logContainer = document.getElementById('log-container');
const entry = document.createElement('div');
entry.className = 'log-entry';
entry.innerHTML = `<span class="log-timestamp">[${timestamp}]</span> ${message}`;
logContainer.appendChild(entry);
logContainer.scrollTop = logContainer.scrollHeight;
}
function clearLog() {
logs.length = 0;
document.getElementById('log-container').innerHTML = '';
log('Log cleared');
}
async function updateServiceWorkerInfo() {
if ('serviceWorker' in navigator) {
const registration = await navigator.serviceWorker.getRegistration();
if (registration) {
const statusBadge = document.getElementById('sw-status');
statusBadge.textContent = 'Active';
statusBadge.className = 'status-badge status-active';
const sw = registration.active || registration.installing || registration.waiting;
if (sw) {
document.getElementById('sw-state').textContent = sw.state;
document.getElementById('sw-url').textContent = sw.scriptURL;
}
document.getElementById('sw-scope').textContent = registration.scope;
if (registration.waiting) {
document.getElementById('sw-update').textContent = 'Yes - Update available!';
log('Service worker update available');
} else {
document.getElementById('sw-update').textContent = 'No';
}
log('Service worker information updated');
} else {
const statusBadge = document.getElementById('sw-status');
statusBadge.textContent = 'Not Registered';
statusBadge.className = 'status-badge status-inactive';
log('No service worker registered');
}
} else {
document.getElementById('sw-status').textContent = 'Not Supported';
log('Service workers not supported in this browser');
}
}
async function checkUpdate() {
log('Checking for service worker updates...');
const registration = await navigator.serviceWorker.getRegistration();
if (registration) {
await registration.update();
log('Update check complete');
updateServiceWorkerInfo();
}
}
async function forceUpdate() {
log('Forcing service worker update...');
const registration = await navigator.serviceWorker.getRegistration();
if (registration && registration.waiting) {
registration.waiting.postMessage({ type: 'SKIP_WAITING' });
log('Update forced, reloading page...');
setTimeout(() => window.location.reload(), 1000);
} else {
log('No update available to force');
}
}
async function unregisterSW() {
if (confirm('Are you sure you want to unregister the service worker?')) {
log('Unregistering service worker...');
const registration = await navigator.serviceWorker.getRegistration();
if (registration) {
await registration.unregister();
log('Service worker unregistered');
updateServiceWorkerInfo();
}
}
}
async function refreshCacheInfo() {
log('Refreshing cache information...');
const cacheNames = await caches.keys();
const cacheInfo = document.getElementById('cache-info');
if (cacheNames.length === 0) {
cacheInfo.innerHTML = '<p>No caches found</p>';
log('No caches found');
return;
}
const cacheStats = await Promise.all(
cacheNames.map(async (name) => {
const cache = await caches.open(name);
const keys = await cache.keys();
return { name, size: keys.length };
})
);
cacheInfo.innerHTML = '<ul class="cache-list">' +
cacheStats.map(stat =>
`<li class="cache-item">
<span class="cache-name">${stat.name}</span>
<span class="cache-size">${stat.size} items</span>
</li>`
).join('') +
'</ul>';
log(`Found ${cacheNames.length} cache(s)`);
}
async function clearAllCaches() {
if (confirm('Are you sure you want to clear all caches? This will remove offline capabilities until the next page load.')) {
log('Clearing all caches...');
const cacheNames = await caches.keys();
await Promise.all(cacheNames.map(name => caches.delete(name)));
log(`Cleared ${cacheNames.length} cache(s)`);
refreshCacheInfo();
}
}
function checkDisplayMode() {
const displayMode = window.matchMedia('(display-mode: standalone)').matches ?
'Standalone (PWA)' : 'Browser';
document.getElementById('display-mode').textContent = displayMode;
log(`Display mode: ${displayMode}`);
}
function installPWA() {
if (deferredPrompt) {
deferredPrompt.prompt();
deferredPrompt.userChoice.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
log('User accepted PWA installation');
} else {
log('User dismissed PWA installation');
}
deferredPrompt = null;
document.getElementById('install-btn').style.display = 'none';
});
}
}
// Listen for install prompt
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
deferredPrompt = e;
document.getElementById('pwa-installable').textContent = 'Yes';
document.getElementById('install-btn').style.display = 'inline-block';
log('PWA installation prompt available');
});
// Listen for successful install
window.addEventListener('appinstalled', () => {
log('PWA successfully installed');
document.getElementById('pwa-installable').textContent = 'Installed';
document.getElementById('install-btn').style.display = 'none';
});
// Initialize on page load
window.addEventListener('load', () => {
log('Page loaded, initializing inspector...');
updateServiceWorkerInfo();
refreshCacheInfo();
checkDisplayMode();
if (window.matchMedia('(display-mode: standalone)').matches) {
document.getElementById('pwa-installable').textContent = 'Already installed';
}
});
// Listen for service worker updates
if ('serviceWorker' in navigator) {
navigator.serviceWorker.addEventListener('controllerchange', () => {
log('Service worker controller changed');
updateServiceWorkerInfo();
});
}
</script>
</body>
</html>