diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/wefax.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/wefax.js
index 5594b0f..e34b12f 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/wefax.js
+++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/wefax.js
@@ -1,23 +1,38 @@
// ---------------------------------------------------------------------------
// wefax.js — WEFAX decoder plugin for trx-frontend-http
+// Live view: decoder state, live canvas, latest image card
+// History view: filterable table of all decoded images
// ---------------------------------------------------------------------------
-// --- DOM refs ---
-var wefaxStatus = document.getElementById('wefax-status');
-var wefaxLiveContainer= document.getElementById('wefax-live-container');
-var wefaxLiveInfo = document.getElementById('wefax-live-info');
-var wefaxLiveCanvas = document.getElementById('wefax-live-canvas');
-var wefaxGallery = document.getElementById('wefax-gallery');
-var wefaxToggleBtn = document.getElementById('wefax-decode-toggle-btn');
-var wefaxClearBtn = document.getElementById('wefax-clear-btn');
+// ── DOM references (cached once) ───────────────────────────────────
+var wefaxDom = {
+ status: document.getElementById('wefax-status'),
+ liveView: document.getElementById('wefax-live-view'),
+ historyView: document.getElementById('wefax-history-view'),
+ liveContainer: document.getElementById('wefax-live-container'),
+ liveInfo: document.getElementById('wefax-live-info'),
+ liveCanvas: document.getElementById('wefax-live-canvas'),
+ liveLatest: document.getElementById('wefax-live-latest'),
+ historyList: document.getElementById('wefax-history-list'),
+ historyCount: document.getElementById('wefax-history-count'),
+ filterInput: document.getElementById('wefax-filter'),
+ sortSelect: document.getElementById('wefax-sort'),
+ toggleBtn: document.getElementById('wefax-decode-toggle-btn'),
+ clearBtn: document.getElementById('wefax-clear-btn'),
+ viewLiveBtn: document.getElementById('wefax-view-live'),
+ viewHistoryBtn: document.getElementById('wefax-view-history'),
+};
-// --- State ---
-var wefaxImageHistory = [];
-var wefaxLiveCtx = null;
+// ── State ───────────────────────────────────────────────────────────
+var wefaxImageHistory = [];
+var WEFAX_MAX_IMAGES = 100;
+var wefaxLiveCtx = null;
var wefaxLiveLineCount = 0;
var wefaxLivePixelsPerLine = 1809;
+var wefaxActiveView = 'live';
+var wefaxFilterText = '';
-// --- Helpers ---
+// ── Helpers ─────────────────────────────────────────────────────────
function currentWefaxHistoryRetentionMs() {
return window.getDecodeHistoryRetentionMs ? window.getDecodeHistoryRetentionMs() : 24 * 60 * 60 * 1000;
}
@@ -35,26 +50,51 @@ function escapeHtml(s) {
.replace(/"/g, '"');
}
-// --- Live canvas rendering ---
+function scheduleWefaxUi(key, job) {
+ if (typeof window.trxScheduleUiFrameJob === 'function') {
+ window.trxScheduleUiFrameJob(key, job);
+ return;
+ }
+ job();
+}
+// ── View switching ──────────────────────────────────────────────────
+function switchWefaxView(view) {
+ wefaxActiveView = view;
+ if (wefaxDom.liveView) wefaxDom.liveView.style.display = view === 'live' ? '' : 'none';
+ if (wefaxDom.historyView) wefaxDom.historyView.style.display = view === 'history' ? '' : 'none';
+
+ [wefaxDom.viewLiveBtn, wefaxDom.viewHistoryBtn].forEach(function (btn) {
+ if (btn) btn.classList.remove('sat-view-active');
+ });
+ if (view === 'live' && wefaxDom.viewLiveBtn) wefaxDom.viewLiveBtn.classList.add('sat-view-active');
+ if (view === 'history' && wefaxDom.viewHistoryBtn) wefaxDom.viewHistoryBtn.classList.add('sat-view-active');
+
+ if (view === 'history') renderWefaxHistoryTable();
+}
+
+if (wefaxDom.viewLiveBtn) wefaxDom.viewLiveBtn.addEventListener('click', function () { switchWefaxView('live'); });
+if (wefaxDom.viewHistoryBtn) wefaxDom.viewHistoryBtn.addEventListener('click', function () { switchWefaxView('history'); });
+
+// ── Live canvas rendering ───────────────────────────────────────────
function resetLiveCanvas(pixelsPerLine) {
wefaxLivePixelsPerLine = pixelsPerLine;
wefaxLiveLineCount = 0;
- wefaxLiveCanvas.width = pixelsPerLine;
- wefaxLiveCanvas.height = 800;
- wefaxLiveCtx = wefaxLiveCanvas.getContext('2d');
+ wefaxDom.liveCanvas.width = pixelsPerLine;
+ wefaxDom.liveCanvas.height = 800;
+ wefaxLiveCtx = wefaxDom.liveCanvas.getContext('2d');
wefaxLiveCtx.fillStyle = '#000';
- wefaxLiveCtx.fillRect(0, 0, wefaxLiveCanvas.width, wefaxLiveCanvas.height);
- wefaxLiveContainer.style.display = '';
+ wefaxLiveCtx.fillRect(0, 0, wefaxDom.liveCanvas.width, wefaxDom.liveCanvas.height);
+ if (wefaxDom.liveContainer) wefaxDom.liveContainer.style.display = '';
}
function paintLine(lineBytes) {
if (!wefaxLiveCtx) return;
var y = wefaxLiveLineCount;
- if (y >= wefaxLiveCanvas.height) {
- var old = wefaxLiveCtx.getImageData(0, 0, wefaxLiveCanvas.width, wefaxLiveCanvas.height);
- wefaxLiveCanvas.height *= 2;
+ if (y >= wefaxDom.liveCanvas.height) {
+ var old = wefaxLiveCtx.getImageData(0, 0, wefaxDom.liveCanvas.width, wefaxDom.liveCanvas.height);
+ wefaxDom.liveCanvas.height *= 2;
wefaxLiveCtx.putImageData(old, 0, 0);
}
@@ -70,59 +110,144 @@ function paintLine(lineBytes) {
wefaxLiveLineCount++;
}
-// --- Gallery rendering ---
+// ── Live view: latest image card ────────────────────────────────────
+function renderWefaxLatestCard() {
+ if (!wefaxDom.liveLatest) return;
+ if (wefaxImageHistory.length === 0) {
+ wefaxDom.liveLatest.innerHTML =
+ '
No images decoded yet. Enable the decoder and tune to a WEFAX station.
';
+ return;
+ }
-function renderGalleryThumbnail(msg) {
- var card = document.createElement('div');
- card.className = 'wefax-card';
- card.style.cssText =
- 'border:1px solid var(--border-color); border-radius:4px; ' +
- 'padding:0.4rem; max-width:280px; cursor:pointer;';
+ var img = wefaxImageHistory[0];
+ var ts = img._ts || '--';
+ var date = img._tsMs ? new Date(img._tsMs).toLocaleDateString() : '';
+ var meta = [
+ img.ioc + ' IOC',
+ img.lpm + ' LPM',
+ img.line_count + ' lines',
+ date + ' ' + ts,
+ ].join(' \u00b7 ');
- var ts = msg._tsMs ? new Date(msg._tsMs).toLocaleString() : '\u2014';
- var info = msg.ioc + ' IOC \u00b7 ' + msg.lpm + ' LPM \u00b7 ' + msg.line_count + ' lines';
-
- var imgSrc = msg._dataUrl
- ? msg._dataUrl
- : msg.path
- ? '/images/' + escapeHtml(msg.path.split('/').pop())
+ var imgSrc = img._dataUrl
+ ? img._dataUrl
+ : img.path
+ ? '/images/' + escapeHtml(img.path.split('/').pop())
: null;
+ var html = '
';
+ html += '
Latest decoded image
';
+ html += '
' + escapeHtml(meta) + '
';
if (imgSrc) {
- card.innerHTML =
- '

' +
- '
' + escapeHtml(ts) + '
' +
- '
' + info + '
';
- } else {
- card.innerHTML =
- '
' + escapeHtml(ts) + '
' +
- '
' + info + '
';
+ html += '
View full image';
}
- return card;
+ html += '
';
+ wefaxDom.liveLatest.innerHTML = html;
}
-function renderWefaxGallery() {
+// ── History view: table ─────────────────────────────────────────────
+function getWefaxFilteredHistory() {
+ var items = wefaxImageHistory;
+
+ if (wefaxFilterText) {
+ items = items.filter(function (i) {
+ var haystack = [
+ String(i.ioc || ''),
+ String(i.lpm || ''),
+ String(i.line_count || ''),
+ ].join(' ').toUpperCase();
+ return haystack.indexOf(wefaxFilterText) >= 0;
+ });
+ }
+
+ var sortVal = wefaxDom.sortSelect ? wefaxDom.sortSelect.value : 'newest';
+ if (sortVal === 'oldest') items = items.slice().reverse();
+
+ return items;
+}
+
+function renderWefaxHistoryRow(img) {
+ var row = document.createElement('div');
+ row.className = 'sat-history-row';
+
+ var ts = img._ts || '--';
+ var date = img._tsMs ? new Date(img._tsMs).toLocaleDateString([], { month: 'short', day: 'numeric' }) : '';
+ var ioc = img.ioc || '--';
+ var lpm = img.lpm || '--';
+ var lines = img.line_count || 0;
+
+ var imgSrc = img._dataUrl
+ ? img._dataUrl
+ : img.path
+ ? '/images/' + escapeHtml(img.path.split('/').pop())
+ : null;
+ var link = imgSrc
+ ? '
View'
+ : '--';
+
+ row.innerHTML = [
+ '
' + escapeHtml(date + ' ' + ts) + '',
+ '
' + escapeHtml(String(ioc)) + '',
+ '
' + escapeHtml(String(lpm)) + '',
+ '
' + lines + '',
+ '
' + link + '',
+ ].join('');
+
+ return row;
+}
+
+function renderWefaxHistoryTable() {
+ if (!wefaxDom.historyList) return;
pruneWefaxHistory();
- var frag = document.createDocumentFragment();
- for (var i = 0; i < wefaxImageHistory.length; i++) {
- frag.appendChild(renderGalleryThumbnail(wefaxImageHistory[i]));
+ var items = getWefaxFilteredHistory();
+ var fragment = document.createDocumentFragment();
+ for (var i = 0; i < items.length; i++) {
+ fragment.appendChild(renderWefaxHistoryRow(items[i]));
}
- wefaxGallery.innerHTML = '';
- wefaxGallery.appendChild(frag);
-}
+ wefaxDom.historyList.replaceChildren(fragment);
-function scheduleWefaxGalleryRender() {
- if (window.trxScheduleUiFrameJob) {
- window.trxScheduleUiFrameJob('wefax-gallery', renderWefaxGallery);
- } else {
- requestAnimationFrame(renderWefaxGallery);
+ if (wefaxDom.historyCount) {
+ var total = wefaxImageHistory.length;
+ var shown = items.length;
+ wefaxDom.historyCount.textContent =
+ total === 0
+ ? 'No images yet'
+ : shown === total
+ ? total + ' image' + (total === 1 ? '' : 's')
+ : shown + ' of ' + total + ' images';
}
}
-// --- SSE event handlers (public API) ---
+// ── Add image to history ────────────────────────────────────────────
+function addWefaxImage(msg) {
+ var tsMs = Number.isFinite(msg.ts_ms) ? Number(msg.ts_ms) : Date.now();
+ msg._tsMs = tsMs;
+ msg._ts = new Date(tsMs).toLocaleTimeString([], {
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit',
+ });
+ // Capture the live canvas as a data URI for thumbnails.
+ if (wefaxLiveCtx && wefaxLiveLineCount > 0) {
+ var trimmed = wefaxLiveCtx.getImageData(0, 0, wefaxDom.liveCanvas.width, wefaxLiveLineCount);
+ wefaxDom.liveCanvas.height = wefaxLiveLineCount;
+ wefaxLiveCtx.putImageData(trimmed, 0, 0);
+ try { msg._dataUrl = wefaxDom.liveCanvas.toDataURL('image/png'); } catch (e) {}
+ }
+
+ wefaxImageHistory.unshift(msg);
+ if (wefaxImageHistory.length > WEFAX_MAX_IMAGES) {
+ wefaxImageHistory = wefaxImageHistory.slice(0, WEFAX_MAX_IMAGES);
+ }
+
+ scheduleWefaxUi('wefax-latest', renderWefaxLatestCard);
+ if (wefaxActiveView === 'history') {
+ scheduleWefaxUi('wefax-history', renderWefaxHistoryTable);
+ }
+}
+
+// ── SSE event handlers (public API) ─────────────────────────────────
window.onServerWefaxProgress = function (msg) {
if (msg.line_count <= 1 || !wefaxLiveCtx) {
resetLiveCanvas(msg.pixels_per_line || 1809);
@@ -135,70 +260,84 @@ window.onServerWefaxProgress = function (msg) {
paintLine(bytes);
}
- if (wefaxLiveInfo) {
- wefaxLiveInfo.textContent =
+ if (wefaxDom.liveInfo) {
+ wefaxDom.liveInfo.textContent =
'Line ' + msg.line_count + ' \u00b7 ' + msg.ioc + ' IOC \u00b7 ' + msg.lpm + ' LPM';
}
- if (wefaxStatus) {
- wefaxStatus.textContent = 'Receiving \u2014 line ' + msg.line_count;
- wefaxStatus.style.color = 'var(--text-accent)';
+ if (wefaxDom.status) {
+ wefaxDom.status.textContent = 'Receiving \u2014 line ' + msg.line_count;
+ wefaxDom.status.style.color = 'var(--text-accent)';
}
};
window.onServerWefax = function (msg) {
- msg._tsMs = msg.ts_ms || Date.now();
+ addWefaxImage(msg);
- // Capture the live canvas as a data URI for gallery thumbnails.
- if (wefaxLiveCtx && wefaxLiveLineCount > 0) {
- var trimmed = wefaxLiveCtx.getImageData(0, 0, wefaxLiveCanvas.width, wefaxLiveLineCount);
- wefaxLiveCanvas.height = wefaxLiveLineCount;
- wefaxLiveCtx.putImageData(trimmed, 0, 0);
- try { msg._dataUrl = wefaxLiveCanvas.toDataURL('image/png'); } catch (e) {}
- }
-
- wefaxImageHistory.unshift(msg);
- pruneWefaxHistory();
- scheduleWefaxGalleryRender();
-
- if (wefaxStatus) {
- wefaxStatus.textContent = 'Complete \u2014 ' + msg.line_count + ' lines';
- wefaxStatus.style.color = '';
+ if (wefaxDom.liveContainer) wefaxDom.liveContainer.style.display = 'none';
+ if (wefaxDom.status) {
+ wefaxDom.status.textContent = 'Complete \u2014 ' + msg.line_count + ' lines';
+ wefaxDom.status.style.color = '';
}
};
window.restoreWefaxHistory = function (messages) {
if (!messages || !messages.length) return;
for (var i = 0; i < messages.length; i++) {
- messages[i]._tsMs = messages[i].ts_ms || Date.now();
+ var tsMs = Number.isFinite(messages[i].ts_ms) ? Number(messages[i].ts_ms) : Date.now();
+ messages[i]._tsMs = tsMs;
+ messages[i]._ts = new Date(tsMs).toLocaleTimeString([], {
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit',
+ });
}
wefaxImageHistory = messages.concat(wefaxImageHistory);
pruneWefaxHistory();
- scheduleWefaxGalleryRender();
+ scheduleWefaxUi('wefax-latest', renderWefaxLatestCard);
+ if (wefaxActiveView === 'history') {
+ scheduleWefaxUi('wefax-history', renderWefaxHistoryTable);
+ }
};
window.pruneWefaxHistoryView = function () {
pruneWefaxHistory();
- scheduleWefaxGalleryRender();
+ renderWefaxHistoryTable();
+ renderWefaxLatestCard();
};
window.resetWefaxHistoryView = function () {
wefaxImageHistory = [];
- if (wefaxGallery) wefaxGallery.innerHTML = '';
- if (wefaxLiveContainer) wefaxLiveContainer.style.display = 'none';
+ if (wefaxDom.historyList) wefaxDom.historyList.innerHTML = '';
+ if (wefaxDom.liveContainer) wefaxDom.liveContainer.style.display = 'none';
wefaxLiveCtx = null;
wefaxLiveLineCount = 0;
- if (wefaxStatus) {
- wefaxStatus.textContent = 'Idle';
- wefaxStatus.style.color = '';
+ renderWefaxLatestCard();
+ renderWefaxHistoryTable();
+ if (wefaxDom.status) {
+ wefaxDom.status.textContent = 'Idle';
+ wefaxDom.status.style.color = '';
}
};
-// --- Button handlers ---
-if (wefaxToggleBtn) {
- wefaxToggleBtn.addEventListener('click', async function () {
+// ── Filter / sort handlers ──────────────────────────────────────────
+if (wefaxDom.filterInput) {
+ wefaxDom.filterInput.addEventListener('input', function () {
+ wefaxFilterText = wefaxDom.filterInput.value.trim().toUpperCase();
+ scheduleWefaxUi('wefax-history', renderWefaxHistoryTable);
+ });
+}
+if (wefaxDom.sortSelect) {
+ wefaxDom.sortSelect.addEventListener('change', function () {
+ scheduleWefaxUi('wefax-history', renderWefaxHistoryTable);
+ });
+}
+
+// ── Button handlers ─────────────────────────────────────────────────
+if (wefaxDom.toggleBtn) {
+ wefaxDom.toggleBtn.addEventListener('click', async function () {
try {
if (window.takeSchedulerControlForDecoderDisable) {
- await window.takeSchedulerControlForDecoderDisable(wefaxToggleBtn);
+ await window.takeSchedulerControlForDecoderDisable(wefaxDom.toggleBtn);
}
await postPath('/toggle_wefax_decode');
} catch (e) {
@@ -206,8 +345,8 @@ if (wefaxToggleBtn) {
}
});
}
-if (wefaxClearBtn) {
- wefaxClearBtn.addEventListener('click', async function () {
+if (wefaxDom.clearBtn) {
+ wefaxDom.clearBtn.addEventListener('click', async function () {
try {
await postPath('/clear_wefax_decode');
window.resetWefaxHistoryView();
@@ -216,3 +355,6 @@ if (wefaxClearBtn) {
}
});
}
+
+// ── Initial render ──────────────────────────────────────────────────
+renderWefaxLatestCard();