Repository navigation
Expand file tree
/
Copy pathscript.js
More file actions
366 lines (338 loc) · 16.4 KB
/
Copy pathscript.js
File metadata and controls
366 lines (338 loc) · 16.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
(() => {
'use strict';
const $ = (selector) => document.querySelector(selector);
const ua = navigator.userAgent;
const samples = { cpu: [], memory: [] };
const historyLimit = 36;
let lastFrame = performance.now();
let diagnosisCount = 0;
let observedBattery;
const browserLabel = () => {
if (/Edg\//.test(ua)) return 'Microsoft Edge';
if (/OPR\//.test(ua)) return 'Opera';
if (/Chrome\//.test(ua) && !/Chromium/.test(ua)) return 'Google Chrome';
if (/Firefox\//.test(ua)) return 'Mozilla Firefox';
if (/Safari\//.test(ua) && !/Chrome\//.test(ua)) return 'Safari';
return 'Web browser';
};
const platformLabel = () => {
const platform = navigator.userAgentData?.platform || navigator.platform || '';
if (/win/i.test(platform)) return 'Windows';
if (/mac/i.test(platform)) return 'macOS';
if (/linux/i.test(platform)) return 'Linux';
if (/android/i.test(ua)) return 'Android';
if (/iphone|ipad/i.test(ua)) return 'iOS';
return platform || 'Unknown platform';
};
const formatBytes = (bytes) => {
if (!Number.isFinite(bytes) || bytes < 0) return 'Unavailable';
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
let value = bytes;
let unit = 0;
while (value >= 1024 && unit < units.length - 1) {
value /= 1024;
unit += 1;
}
return `${value.toFixed(unit > 1 ? 1 : 0)} ${units[unit]}`;
};
const drawChart = (canvas, values, color) => {
const rect = canvas.getBoundingClientRect();
if (!rect.width || !rect.height) return;
const ratio = window.devicePixelRatio || 1;
canvas.width = Math.round(rect.width * ratio);
canvas.height = Math.round(rect.height * ratio);
const context = canvas.getContext('2d');
context.scale(ratio, ratio);
const width = rect.width;
const height = rect.height;
const points = values.length > 1 ? values : [0, ...values];
const max = Math.max(100, ...points);
const coordinates = points.map((value, index) => ({
x: (index / (points.length - 1)) * width,
y: height - 4 - (Math.min(value, max) / max) * (height - 9)
}));
context.strokeStyle = '#2a2b33';
context.lineWidth = 1;
context.setLineDash([3, 4]);
context.beginPath();
context.moveTo(0, height - 1);
context.lineTo(width, height - 1);
context.stroke();
context.setLineDash([]);
const gradient = context.createLinearGradient(0, 0, 0, height);
gradient.addColorStop(0, `${color}38`);
gradient.addColorStop(1, `${color}00`);
context.beginPath();
coordinates.forEach(({ x, y }, index) => index ? context.lineTo(x, y) : context.moveTo(x, y));
context.lineTo(width, height);
context.lineTo(0, height);
context.closePath();
context.fillStyle = gradient;
context.fill();
context.beginPath();
coordinates.forEach(({ x, y }, index) => index ? context.lineTo(x, y) : context.moveTo(x, y));
context.strokeStyle = color;
context.lineWidth = 1.8;
context.lineJoin = 'round';
context.lineCap = 'round';
context.stroke();
};
const record = (key, value) => {
samples[key].push(value);
if (samples[key].length > historyLimit) samples[key].shift();
};
const updateResponsiveness = () => {
const now = performance.now();
const interval = now - lastFrame;
lastFrame = now;
const delay = Math.max(0, interval - 1000);
const score = Math.min(100, Math.round((delay / 500) * 100));
record('cpu', score);
$('#cpu-value').textContent = delay < 15 ? 'Smooth' : delay < 80 ? 'Fair' : 'Delayed';
$('#cpu-foot').textContent = 'Tab responsiveness, not system CPU usage';
drawChart($('#cpu-chart'), samples.cpu, '#a28bff');
};
const updateMemory = () => {
const memory = performance.memory;
if (!memory || !Number.isFinite(memory.usedJSHeapSize)) {
$('#memory-value').textContent = 'N/A';
$('#memory-foot').textContent = 'This browser does not expose JavaScript heap usage';
drawChart($('#memory-chart'), samples.memory, '#71a7ff');
return;
}
const used = memory.usedJSHeapSize;
const limit = memory.jsHeapSizeLimit;
const percent = limit ? Math.round((used / limit) * 100) : 0;
record('memory', percent);
$('#memory-value').textContent = formatBytes(used);
$('#memory-foot').textContent = limit ? `${percent}% of this tab’s heap limit` : 'JavaScript heap estimate for this tab';
drawChart($('#memory-chart'), samples.memory, '#71a7ff');
};
const updateStorage = async () => {
if (!navigator.storage?.estimate) {
$('#storage-value').textContent = 'N/A';
$('#storage-foot').textContent = 'Browser storage estimates are not available';
return null;
}
try {
const estimate = await navigator.storage.estimate();
const usage = estimate.usage;
const quota = estimate.quota;
if (!Number.isFinite(usage) || !Number.isFinite(quota) || quota <= 0) {
$('#storage-value').textContent = 'N/A';
$('#storage-foot').textContent = 'This browser did not return a storage estimate';
return null;
}
const percent = Math.min(100, Math.round((usage / quota) * 100));
$('#storage-value').textContent = formatBytes(usage);
$('#storage-foot').textContent = `${percent}% of this site’s estimated browser quota`;
$('#storage-bar').style.width = `${percent}%`;
$('#storage-bar').style.background = percent > 85 ? 'linear-gradient(90deg, #d99949, #f1bc6c)' : '';
return { usage, quota, percent };
} catch (error) {
$('#storage-value').textContent = 'Unavailable';
$('#storage-foot').textContent = 'Browser blocked access to its storage estimate';
return null;
}
};
const updateConnection = () => {
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
$('#network-value').textContent = navigator.onLine ? 'Online' : 'Offline';
const cutoff = performance.now() - 60000;
const recentResources = performance.getEntriesByType('resource').filter((entry) => entry.responseEnd >= cutoff);
const measurableBytes = recentResources.reduce((total, entry) => (
total + (Number.isFinite(entry.transferSize) ? entry.transferSize : 0)
), 0);
const traffic = recentResources.length
? measurableBytes
? `${formatBytes(measurableBytes)} page resources / 60s`
: `${recentResources.length} recent requests · sizes restricted`
: 'No recent page resources';
if (connection) {
const type = connection.effectiveType ? connection.effectiveType.toUpperCase() : 'Connected';
const downlink = Number.isFinite(connection.downlink) ? ` · ~${connection.downlink} Mbps` : '';
$('#network-detail').textContent = `${traffic} · ${type}${downlink}${connection.saveData ? ' · data saver' : ''}`;
} else {
$('#network-detail').textContent = `${traffic} · connection type not shared`;
}
};
const updateBattery = async () => {
if (!navigator.getBattery) {
$('#battery-value').textContent = 'N/A';
$('#battery-detail').textContent = 'Battery details aren’t available in this browser';
$('#access-battery').textContent = 'Limited';
return null;
}
try {
const battery = await navigator.getBattery();
const render = () => {
const percent = Math.round(battery.level * 100);
const charging = battery.charging ? ' · charging' : '';
$('#battery-value').textContent = `${percent}%${charging}`;
$('#battery-detail').textContent = battery.charging ? 'Power connected' : 'On battery power';
};
render();
if (observedBattery !== battery) {
battery.addEventListener('levelchange', render);
battery.addEventListener('chargingchange', render);
observedBattery = battery;
}
$('#access-battery').textContent = 'Available';
return battery;
} catch (error) {
$('#battery-value').textContent = 'Unavailable';
$('#battery-detail').textContent = 'Browser denied access to battery status';
$('#access-battery').textContent = 'Limited';
return null;
}
};
const signal = (severity, title, description) => ({ severity, title, description });
const renderFindings = (items) => {
const container = $('#finding-list');
container.replaceChildren();
if (!items.length) {
container.innerHTML = '<div class="empty-state">No browser-visible issues found. System-wide checks are restricted.</div>';
return;
}
items.forEach((item, index) => {
const row = document.createElement('div');
row.className = 'finding-item';
row.style.animationDelay = `${index * 55}ms`;
const icon = document.createElement('span');
icon.className = `finding-severity ${item.severity}`;
icon.textContent = item.severity === 'good' ? '✓' : item.severity === 'warning' ? '!' : 'i';
const copy = document.createElement('div');
const title = document.createElement('strong');
title.textContent = item.title;
const description = document.createElement('p');
description.textContent = item.description;
copy.append(title, description);
row.append(icon, copy);
container.append(row);
});
$('#finding-count').textContent = `${items.length} ${items.length === 1 ? 'item' : 'items'}`;
};
const renderRecommendations = (items) => {
const container = $('#recommendation-list');
container.replaceChildren();
items.slice(0, 3).forEach((item, index) => {
const row = document.createElement('div');
row.className = 'recommendation-item';
const number = document.createElement('span');
number.className = 'recommendation-number';
number.textContent = String(index + 1).padStart(2, '0');
const copy = document.createElement('div');
const title = document.createElement('strong');
title.textContent = item.title;
const description = document.createElement('p');
description.textContent = item.description;
copy.append(title, description);
const arrow = document.createElement('span');
arrow.className = 'recommendation-arrow';
arrow.textContent = '↗';
row.append(number, copy, arrow);
container.append(row);
});
};
const setHealth = (score, title, description, label, color) => {
$('#health-score').textContent = score;
$('#health-title').textContent = title;
$('#health-description').textContent = description;
$('#health-label').textContent = label;
const ring = $('#health-ring');
ring.style.strokeDashoffset = String(371 - (371 * score / 100));
ring.style.stroke = color;
};
const diagnose = async () => {
const button = $('#diagnose-button');
button.disabled = true;
button.classList.add('is-scanning');
button.querySelector('.button-label').textContent = 'Checking your browser…';
$('#device-status').textContent = 'Diagnosing';
await new Promise((resolve) => setTimeout(resolve, 550));
const [storage, battery] = await Promise.all([updateStorage(), updateBattery()]);
updateConnection();
updateMemory();
const findings = [];
const recommendations = [];
let score = 100;
if (!navigator.onLine) {
findings.push(signal('warning', 'This device appears offline', 'Your browser reports no network connection. Check Wi-Fi or your network cable.'));
recommendations.push({ title: 'Reconnect to your network', description: 'Check Wi-Fi or ethernet and retry any pages that are not loading.' });
score -= 18;
} else {
findings.push(signal('good', 'Network connection is available', 'Your browser can reach the network. Other apps’ traffic and actual throughput are not visible here.'));
}
if (storage?.percent > 85) {
findings.push(signal('warning', 'Browser storage is getting full', `This site is using about ${storage.percent}% of its estimated browser storage quota. This is not your device’s disk usage.`));
recommendations.push({ title: 'Review this site’s stored data', description: 'Browser settings let you inspect and clear saved site data if it is no longer needed.' });
score -= 12;
} else if (storage) {
findings.push(signal('good', 'Browser storage has room', `This site is using about ${storage.percent}% of its estimated browser quota; whole-disk free space is not available to web pages.`));
} else {
findings.push(signal('info', 'Storage estimate unavailable', 'The browser did not provide site storage usage. System disk capacity and free space are restricted.'));
score -= 3;
}
if (performance.memory) {
const ratio = performance.memory.usedJSHeapSize / performance.memory.jsHeapSizeLimit;
if (ratio > 0.75) {
findings.push(signal('warning', 'This tab is using a large JavaScript heap', 'A complex page can use more memory. This is only this tab’s JavaScript heap, not total system RAM.'));
recommendations.push({ title: 'Refresh memory-heavy pages', description: 'Save your work, then reload tabs that have been open for a long time.' });
score -= 10;
} else {
findings.push(signal('good', 'This tab’s JavaScript heap looks comfortable', 'This browser-specific estimate covers this tab only; total RAM use is hidden by browser security.'));
}
} else {
findings.push(signal('info', 'System RAM cannot be measured here', 'This browser does not expose JavaScript heap metrics, and websites cannot read total RAM usage.'));
score -= 3;
}
findings.push(signal('info', 'System CPU and running processes are private', 'Web pages cannot inspect processor load, background processes, startup apps, other tabs, or extensions.'));
recommendations.push({ title: 'Use your system’s task manager', description: `Open ${platformLabel() === 'Windows' ? 'Task Manager' : platformLabel() === 'macOS' ? 'Activity Monitor' : 'your system monitor'} to see CPU, memory, processes, and startup apps.` });
score = Math.max(0, score);
if (score >= 85) {
setHealth(score, 'Looking good so far', 'The signals available to this browser look healthy. System-wide usage remains private.', 'Healthy', '#6ed9a4');
} else if (score >= 70) {
setHealth(score, 'A couple of things to check', 'We found a browser-visible signal worth a closer look. This is not a full system scan.', 'Worth a look', '#f3bc6c');
} else {
setHealth(score, 'Some attention may help', 'One or more browser-visible signals may be affecting this session. Check the details below.', 'Needs attention', '#fb7d87');
}
renderFindings(findings);
renderRecommendations(recommendations);
$('#device-status').textContent = 'Monitoring';
$('#last-check').textContent = new Date().toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' });
$('#device-description').textContent = `Browser-only diagnostics · ${platformLabel()} · private to this device`;
diagnosisCount += 1;
button.disabled = false;
button.classList.remove('is-scanning');
button.querySelector('.button-label').textContent = diagnosisCount > 1 ? 'Run diagnosis again' : 'Diagnose my computer';
};
const init = () => {
$('#browser-name').textContent = browserLabel();
$('#platform-name').textContent = platformLabel();
$('#device-name').textContent = `${platformLabel()} device`;
$('#device-description').textContent = `Browser-only diagnostics · ${platformLabel()} · private to this device`;
$('#top-date').textContent = new Date().toLocaleDateString([], { weekday: 'short', month: 'short', day: 'numeric' });
updateConnection();
updateBattery();
updateMemory();
updateResponsiveness();
window.setInterval(updateResponsiveness, 1000);
window.setInterval(updateMemory, 5000);
window.setInterval(updateConnection, 5000);
(navigator.connection || navigator.mozConnection || navigator.webkitConnection)?.addEventListener('change', updateConnection);
window.addEventListener('resize', () => {
drawChart($('#cpu-chart'), samples.cpu, '#a28bff');
drawChart($('#memory-chart'), samples.memory, '#71a7ff');
});
window.addEventListener('online', updateConnection);
window.addEventListener('offline', updateConnection);
$('#diagnose-button').addEventListener('click', diagnose);
document.querySelectorAll('.nav-item').forEach((link) => {
link.addEventListener('click', () => {
document.querySelector('.nav-item.active')?.classList.remove('active');
link.classList.add('active');
});
});
};
init();
})();