[0.2.0] - 2026-09-24 #1
@@ -3,4 +3,5 @@
|
|||||||
### 🚀 Features
|
### 🚀 Features
|
||||||
|
|
||||||
- Added changelog
|
- Added changelog
|
||||||
|
- Added Themes
|
||||||
## [0.1.0] - 2026-09-23
|
## [0.1.0] - 2026-09-23
|
||||||
|
|||||||
+90
-23
@@ -19,7 +19,7 @@ var theme = (() => {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
localStorage.setItem(STORAGE_KEY, name);
|
localStorage.setItem(STORAGE_KEY, name);
|
||||||
} catch (e) {}
|
} catch (e) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
@@ -28,7 +28,7 @@ var theme = (() => {
|
|||||||
|
|
||||||
var picker = document.getElementById('theme-picker');
|
var picker = document.getElementById('theme-picker');
|
||||||
picker.value = saved;
|
picker.value = saved;
|
||||||
picker.addEventListener('change', function() {
|
picker.addEventListener('change', function () {
|
||||||
set(picker.value);
|
set(picker.value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -39,12 +39,18 @@ var theme = (() => {
|
|||||||
return { init };
|
return { init };
|
||||||
})();
|
})();
|
||||||
const table = (() => {
|
const table = (() => {
|
||||||
|
var dirty = false;
|
||||||
|
var flushTimer = null;
|
||||||
|
var ipIndex = {};
|
||||||
let data = [];
|
let data = [];
|
||||||
let filtered = [];
|
let filtered = [];
|
||||||
let sortKey = 'ip';
|
let sortKey = 'ip';
|
||||||
let sortDir = 'asc';
|
let sortDir = 'asc';
|
||||||
let filterText = '';
|
let filterText = '';
|
||||||
let checkedIps = new Set();
|
let checkedIps = new Set();
|
||||||
|
var ROW_HEIGHT = 32;
|
||||||
|
var BUFFER_ROWS = 20;
|
||||||
|
var scrollContainer = null;
|
||||||
|
|
||||||
function ipToNum(ip) {
|
function ipToNum(ip) {
|
||||||
const parts = String(ip).split('.');
|
const parts = String(ip).split('.');
|
||||||
@@ -193,20 +199,61 @@ const table = (() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
const tbody = document.getElementById('results-body');
|
var tbody = document.getElementById('results-body');
|
||||||
const count = document.getElementById('host-count');
|
var count = document.getElementById('host-count');
|
||||||
if (!tbody) return;
|
if (!tbody) return;
|
||||||
|
|
||||||
expandedIp = null;
|
expandedIp = null;
|
||||||
|
|
||||||
const rows = filtered.map((host) => {
|
if (!scrollContainer) {
|
||||||
const ip = host.ip;
|
scrollContainer = document.getElementById('results-table-container');
|
||||||
const status = host.status || 'Unknown';
|
scrollContainer.addEventListener('scroll', onScroll);
|
||||||
const cls = 'status-' + status.toLowerCase();
|
}
|
||||||
const rtt = host.ping ? host.ping.rtt_ms.toFixed(1) : '-';
|
|
||||||
const hostname = host.hostname || '-';
|
|
||||||
|
|
||||||
const isChecked = checkedIps.has(ip) ? 'checked' : '';
|
renderVisible();
|
||||||
return '<tr data-ip="' + ip + '">'
|
|
||||||
|
if (count) {
|
||||||
|
var up = data.filter(function (h) { return h.status === 'Up'; }).length;
|
||||||
|
count.textContent = up + ' up / ' + data.length + ' total';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (columns && columns.apply) columns.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderVisible() {
|
||||||
|
var tbody = document.getElementById('results-body');
|
||||||
|
if (!tbody || !scrollContainer) return;
|
||||||
|
|
||||||
|
var totalRows = filtered.length;
|
||||||
|
var viewHeight = scrollContainer.clientHeight;
|
||||||
|
var scrollTop = scrollContainer.scrollTop;
|
||||||
|
var headerHeight = 36;
|
||||||
|
|
||||||
|
var startIdx = Math.max(0, Math.floor((scrollTop - headerHeight) / ROW_HEIGHT) - BUFFER_ROWS);
|
||||||
|
var visibleCount = Math.ceil(viewHeight / ROW_HEIGHT) + (BUFFER_ROWS * 2);
|
||||||
|
var endIdx = Math.min(totalRows, startIdx + visibleCount);
|
||||||
|
|
||||||
|
var topSpace = startIdx * ROW_HEIGHT;
|
||||||
|
var bottomSpace = (totalRows - endIdx) * ROW_HEIGHT;
|
||||||
|
|
||||||
|
var colCount = document.querySelectorAll('#results-table th').length;
|
||||||
|
|
||||||
|
var html = '';
|
||||||
|
|
||||||
|
if (topSpace > 0) {
|
||||||
|
html += '<tr class="virtual-spacer"><td colspan="' + colCount + '" style="height:' + topSpace + 'px"></td></tr>';
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = startIdx; i < endIdx; i++) {
|
||||||
|
var host = filtered[i];
|
||||||
|
var ip = host.ip;
|
||||||
|
var status = host.status || 'Unknown';
|
||||||
|
var cls = 'status-' + status.toLowerCase();
|
||||||
|
var rtt = host.ping ? host.ping.rtt_ms.toFixed(1) : '-';
|
||||||
|
var hostname = host.hostname || '-';
|
||||||
|
var isChecked = checkedIps.has(ip) ? 'checked' : '';
|
||||||
|
|
||||||
|
html += '<tr data-ip="' + ip + '">'
|
||||||
+ '<td class="col-check"><input type="checkbox" class="row-check" ' + isChecked + ' /></td>'
|
+ '<td class="col-check"><input type="checkbox" class="row-check" ' + isChecked + ' /></td>'
|
||||||
+ '<td>' + ip + '</td>'
|
+ '<td>' + ip + '</td>'
|
||||||
+ '<td class="' + cls + '">' + status + '</td>'
|
+ '<td class="' + cls + '">' + status + '</td>'
|
||||||
@@ -214,24 +261,43 @@ const table = (() => {
|
|||||||
+ '<td>' + hostname + '</td>'
|
+ '<td>' + hostname + '</td>'
|
||||||
+ '<td class="ports-cell">' + formatPorts(host) + '</td>'
|
+ '<td class="ports-cell">' + formatPorts(host) + '</td>'
|
||||||
+ '</tr>';
|
+ '</tr>';
|
||||||
});
|
|
||||||
|
|
||||||
tbody.innerHTML = rows.join('');
|
|
||||||
|
|
||||||
if (count) {
|
|
||||||
const up = data.filter((h) => h.status === 'Up').length;
|
|
||||||
count.textContent = up + ' up / ' + data.length + ' total';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bottomSpace > 0) {
|
||||||
|
html += '<tr class="virtual-spacer"><td colspan="' + colCount + '" style="height:' + bottomSpace + 'px"></td></tr>';
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody.innerHTML = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
var scrollTimeout = null;
|
||||||
|
function onScroll() {
|
||||||
|
if (scrollTimeout) return;
|
||||||
|
scrollTimeout = requestAnimationFrame(function () {
|
||||||
|
scrollTimeout = null;
|
||||||
|
renderVisible();
|
||||||
if (columns && columns.apply) columns.apply();
|
if (columns && columns.apply) columns.apply();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function upsert(host) {
|
function upsert(host) {
|
||||||
const idx = data.findIndex((h) => h.ip === host.ip);
|
var idx = data.findIndex(function (h) { return h.ip === host.ip; });
|
||||||
if (idx >= 0) {
|
if (idx >= 0) {
|
||||||
data[idx] = host;
|
data[idx] = host;
|
||||||
} else {
|
} else {
|
||||||
|
ipIndex[host.ip] = data.length;
|
||||||
data.push(host);
|
data.push(host);
|
||||||
}
|
}
|
||||||
|
dirty = true;
|
||||||
|
if (!flushTimer) {
|
||||||
|
flushTimer = setTimeout(flush, 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function flush() {
|
||||||
|
flushTimer = null;
|
||||||
|
if (!dirty) return;
|
||||||
|
dirty = false;
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,6 +305,7 @@ const table = (() => {
|
|||||||
data = [];
|
data = [];
|
||||||
filtered = [];
|
filtered = [];
|
||||||
checkedIps.clear();
|
checkedIps.clear();
|
||||||
|
ipIndex = {};
|
||||||
document.getElementById('check-all').checked = false;
|
document.getElementById('check-all').checked = false;
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
@@ -1061,7 +1128,7 @@ var updater = (() => {
|
|||||||
return { init };
|
return { init };
|
||||||
})();
|
})();
|
||||||
var changelog = (() => {
|
var changelog = (() => {
|
||||||
var modal = function() { return document.getElementById('changelog-modal'); };
|
var modal = function () { return document.getElementById('changelog-modal'); };
|
||||||
var loaded = false;
|
var loaded = false;
|
||||||
|
|
||||||
async function open() {
|
async function open() {
|
||||||
@@ -1085,11 +1152,11 @@ var changelog = (() => {
|
|||||||
document.getElementById('btn-changelog').addEventListener('click', open);
|
document.getElementById('btn-changelog').addEventListener('click', open);
|
||||||
document.getElementById('changelog-close').addEventListener('click', close);
|
document.getElementById('changelog-close').addEventListener('click', close);
|
||||||
|
|
||||||
modal().addEventListener('click', function(e) {
|
modal().addEventListener('click', function (e) {
|
||||||
if (e.target === modal()) close();
|
if (e.target === modal()) close();
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('keydown', function(e) {
|
document.addEventListener('keydown', function (e) {
|
||||||
if (e.key === 'Escape' && modal().classList.contains('visible')) close();
|
if (e.key === 'Escape' && modal().classList.contains('visible')) close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -409,6 +409,7 @@ body {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
height: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#results-table tbody tr {
|
#results-table tbody tr {
|
||||||
@@ -428,6 +429,15 @@ body {
|
|||||||
box-shadow: inset 3px 0 0 var(--cyan);
|
box-shadow: inset 3px 0 0 var(--cyan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.virtual-spacer {
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.virtual-spacer td {
|
||||||
|
padding: 0 !important;
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* ===== Status Colors ===== */
|
/* ===== Status Colors ===== */
|
||||||
|
|
||||||
.status-up {
|
.status-up {
|
||||||
@@ -1322,6 +1332,7 @@ body {
|
|||||||
.changelog-content a:hover {
|
.changelog-content a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-picker {
|
.theme-picker {
|
||||||
padding: 1px 6px;
|
padding: 1px 6px;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
@@ -1345,6 +1356,7 @@ body {
|
|||||||
background: var(--bg-secondary);
|
background: var(--bg-secondary);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== Theme: Midnight ===== */
|
/* ===== Theme: Midnight ===== */
|
||||||
|
|
||||||
:root[data-theme="midnight"] {
|
:root[data-theme="midnight"] {
|
||||||
|
|||||||
Reference in New Issue
Block a user