[0.2.0] - 2026-09-24 #1

Merged
mysticmomba merged 7 commits from development into master 2026-09-24 10:18:37 -04:00
3 changed files with 104 additions and 24 deletions
Showing only changes of commit adc061ae26 - Show all commits
+1
View File
@@ -3,4 +3,5 @@
### 🚀 Features
- Added changelog
- Added Themes
## [0.1.0] - 2026-09-23
+91 -24
View File
@@ -19,7 +19,7 @@ var theme = (() => {
}
try {
localStorage.setItem(STORAGE_KEY, name);
} catch (e) {}
} catch (e) { }
}
function init() {
@@ -28,7 +28,7 @@ var theme = (() => {
var picker = document.getElementById('theme-picker');
picker.value = saved;
picker.addEventListener('change', function() {
picker.addEventListener('change', function () {
set(picker.value);
});
}
@@ -39,12 +39,18 @@ var theme = (() => {
return { init };
})();
const table = (() => {
var dirty = false;
var flushTimer = null;
var ipIndex = {};
let data = [];
let filtered = [];
let sortKey = 'ip';
let sortDir = 'asc';
let filterText = '';
let checkedIps = new Set();
var ROW_HEIGHT = 32;
var BUFFER_ROWS = 20;
var scrollContainer = null;
function ipToNum(ip) {
const parts = String(ip).split('.');
@@ -193,20 +199,61 @@ const table = (() => {
}
function render() {
const tbody = document.getElementById('results-body');
const count = document.getElementById('host-count');
var tbody = document.getElementById('results-body');
var count = document.getElementById('host-count');
if (!tbody) return;
expandedIp = null;
const rows = filtered.map((host) => {
const ip = host.ip;
const status = host.status || 'Unknown';
const cls = 'status-' + status.toLowerCase();
const rtt = host.ping ? host.ping.rtt_ms.toFixed(1) : '-';
const hostname = host.hostname || '-';
if (!scrollContainer) {
scrollContainer = document.getElementById('results-table-container');
scrollContainer.addEventListener('scroll', onScroll);
}
const isChecked = checkedIps.has(ip) ? 'checked' : '';
return '<tr data-ip="' + ip + '">'
renderVisible();
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>' + ip + '</td>'
+ '<td class="' + cls + '">' + status + '</td>'
@@ -214,24 +261,43 @@ const table = (() => {
+ '<td>' + hostname + '</td>'
+ '<td class="ports-cell">' + formatPorts(host) + '</td>'
+ '</tr>';
});
tbody.innerHTML = rows.join('');
if (count) {
const up = data.filter((h) => h.status === 'Up').length;
count.textContent = up + ' up / ' + data.length + ' total';
}
if (columns && columns.apply) columns.apply();
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();
});
}
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) {
data[idx] = host;
} else {
ipIndex[host.ip] = data.length;
data.push(host);
}
dirty = true;
if (!flushTimer) {
flushTimer = setTimeout(flush, 200);
}
}
function flush() {
flushTimer = null;
if (!dirty) return;
dirty = false;
refresh();
}
@@ -239,6 +305,7 @@ const table = (() => {
data = [];
filtered = [];
checkedIps.clear();
ipIndex = {};
document.getElementById('check-all').checked = false;
refresh();
}
@@ -1061,7 +1128,7 @@ var updater = (() => {
return { init };
})();
var changelog = (() => {
var modal = function() { return document.getElementById('changelog-modal'); };
var modal = function () { return document.getElementById('changelog-modal'); };
var loaded = false;
async function open() {
@@ -1085,11 +1152,11 @@ var changelog = (() => {
document.getElementById('btn-changelog').addEventListener('click', open);
document.getElementById('changelog-close').addEventListener('click', close);
modal().addEventListener('click', function(e) {
modal().addEventListener('click', function (e) {
if (e.target === modal()) close();
});
document.addEventListener('keydown', function(e) {
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && modal().classList.contains('visible')) close();
});
}
+12
View File
@@ -409,6 +409,7 @@ body {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
height: 32px;
}
#results-table tbody tr {
@@ -428,6 +429,15 @@ body {
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-up {
@@ -1322,6 +1332,7 @@ body {
.changelog-content a:hover {
text-decoration: underline;
}
.theme-picker {
padding: 1px 6px;
font-size: 11px;
@@ -1345,6 +1356,7 @@ body {
background: var(--bg-secondary);
color: var(--text-primary);
}
/* ===== Theme: Midnight ===== */
:root[data-theme="midnight"] {