diff --git a/Plugin/unraid/pages/rsync.php b/Plugin/unraid/pages/rsync.php
index 33cf893..ab0a7e0 100644
--- a/Plugin/unraid/pages/rsync.php
+++ b/Plugin/unraid/pages/rsync.php
@@ -43,6 +43,16 @@
.vv-ry-run-dur { font-size:10px;color:#555;text-align:right; }
.vv-ry-run-st { text-align:right; }
+/* Sync log card */
+.vv-ry-slog-row { display:grid;grid-template-columns:38px 1fr 70px 50px 46px;gap:6px;align-items:center;
+ padding:3px 0;border-bottom:1px solid #111; }
+.vv-ry-slog-row:last-child { border-bottom:none; }
+.vv-ry-slog-name { font-size:11px;color:#888;overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }
+.vv-ry-slog-dt { font-size:10px;color:#3a3a3a; }
+.vv-ry-slog-dur { font-size:10px;color:#555;text-align:right; }
+.vv-ry-slog-byt { font-size:10px;color:#555;text-align:right; }
+.vv-ry-slog-scrl { overflow-y:auto;max-height:200px; }
+
/* Settings */
.vv-ry-set-grid { display:grid;grid-template-columns:repeat(2,1fr);gap:8px;margin-top:6px; }
.vv-ry-toggle { display:inline-flex;align-items:center;gap:8px;cursor:pointer;user-select:none; }
@@ -720,6 +730,48 @@ function _bwSection(data) {
`;
}
+// ── Sync log card ─────────────────────────────────────────────────────────────
+let _liveTicker = null;
+let _fastPollTimer = null;
+
+function _syncLogCard(data) {
+ const active = data.active || [];
+ const history = data.bw_history || [];
+ const recent = [...history].reverse().slice(0, 14);
+ const now = Math.floor(Date.now() / 1000);
+
+ const activeRows = active.map(a => {
+ const startedTs = now - (a.elapsed || 0);
+ return `
+ LIVE
+ ${_esc(a.profile)}
+ ${_dur(a.elapsed || 0)}
+
`;
+ }).join('');
+
+ const histRows = recent.map(r => {
+ const cls = r.status === 'success' ? 'on' : 'err';
+ const bytes = r.bytes > 0 ? _fmtBytes(r.bytes) : '';
+ return `
+ ${r.status === 'success' ? 'ok' : 'fail'}
+ ${_esc(r.profile)}
+ ${r.date} ${r.time.slice(0,5)}
+ ${_dur(r.duration)}
+ ${bytes}
+
`;
+ }).join('');
+
+ return `
+
Sync Activity
+ ${activeRows}
+ ${activeRows && histRows ? '
' : ''}
+
+ ${histRows || '
No sync history
'}
+
+
`;
+}
+
// ── Settings section ──────────────────────────────────────────────────────────
function _toggle(name, currentVal, label) {
const id = 'vv-ry-tog-' + name;
@@ -800,9 +852,30 @@ function _render(data) {
html += _lastSyncCard(data);
html += _windowsRow(data);
html += _bwSection(data);
+ html += _syncLogCard(data);
html += _settingsSection(data);
document.getElementById('vv-ry-grid').innerHTML = html;
+ // Live elapsed ticker — updates every second without re-fetching
+ if (_liveTicker) { clearInterval(_liveTicker); _liveTicker = null; }
+ if ((data.active || []).length) {
+ _liveTicker = setInterval(() => {
+ document.querySelectorAll('.vv-ry-elapsed').forEach(el => {
+ const s = parseInt(el.dataset.started, 10);
+ if (s) el.textContent = _dur(Math.floor(Date.now() / 1000) - s);
+ });
+ }, 1000);
+ }
+
+ // Fast-poll (5s) while syncs are active; return to 30s when idle
+ const hasActive = (data.active || []).length > 0;
+ if (hasActive && !_fastPollTimer) {
+ _fastPollTimer = setInterval(vvRyLoad, 5000);
+ } else if (!hasActive && _fastPollTimer) {
+ clearInterval(_fastPollTimer);
+ _fastPollTimer = null;
+ }
+
// Restore any window panels that were open before the re-render
_vvRyWinOpen.forEach(key => {
const card = document.querySelector(`.vv-ry-win[data-win-key="${key}"]`);