diff --git a/solixauto/monitor.py b/solixauto/monitor.py
index 50ed457..e5d77c7 100644
--- a/solixauto/monitor.py
+++ b/solixauto/monitor.py
@@ -581,6 +581,16 @@ PAGE = """
color: var(--ink-soft);
}
+ .serial-tag {
+ cursor: pointer;
+ user-select: none;
+ }
+
+ .serial-tag:hover, .serial-tag:focus-visible {
+ color: var(--ink);
+ outline: none;
+ }
+
.readouts {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(108px, 1fr));
@@ -1240,11 +1250,71 @@ function nameCell(d, kind, cls) {
return '' + esc(d.name) + '';
}
+const serialRevealUntil = new Map();
+
+function maskSerial(serial) {
+ if (serial.length <= 5) return serial;
+ return '*****' + serial.slice(-5);
+}
+
function serialTag(d) {
if (!d.serial) return '';
- return '(' + esc(d.serial) + ')';
+ const revealed = (serialRevealUntil.get(d.serial) || 0) > Date.now();
+ const shown = revealed ? d.serial : maskSerial(d.serial);
+ return '('
+ + esc(shown) + ')';
}
+function scheduleSerialRemask(serial) {
+ setTimeout(() => {
+ if ((serialRevealUntil.get(serial) || 0) <= Date.now()) {
+ serialRevealUntil.delete(serial);
+ document.querySelectorAll('.serial-tag').forEach(span => {
+ if (span.dataset.serial === serial && !span.matches(':hover')) {
+ span.textContent = '(' + maskSerial(serial) + ')';
+ span.dataset.revealed = '0';
+ }
+ });
+ }
+ }, 5100);
+}
+
+function revealSerialClick(span) {
+ const serial = span.dataset.serial;
+ if (!serial) return;
+ serialRevealUntil.set(serial, Date.now() + 5000);
+ span.textContent = '(' + serial + ')';
+ span.dataset.revealed = '1';
+ scheduleSerialRemask(serial);
+}
+
+document.addEventListener('mouseenter', event => {
+ const span = event.target.closest && event.target.closest('.serial-tag');
+ if (span) span.textContent = '(' + span.dataset.serial + ')';
+}, true);
+
+document.addEventListener('mouseleave', event => {
+ const span = event.target.closest && event.target.closest('.serial-tag');
+ if (span && span.dataset.revealed !== '1') {
+ span.textContent = '(' + maskSerial(span.dataset.serial) + ')';
+ }
+}, true);
+
+document.addEventListener('click', event => {
+ const span = event.target.closest('.serial-tag');
+ if (span) revealSerialClick(span);
+});
+
+document.addEventListener('keydown', event => {
+ if (event.key !== 'Enter' && event.key !== ' ') return;
+ const span = event.target.closest && event.target.closest('.serial-tag');
+ if (!span) return;
+ event.preventDefault();
+ revealSerialClick(span);
+});
+
function startEdit(span) {
if (span.dataset.editing) return;
span.dataset.editing = '1';