diff --git a/dist/urbm-2026.06.22.r001-x86_64-1.txz.sha256 b/dist/urbm-2026.06.22.r001-x86_64-1.txz.sha256 deleted file mode 100644 index 0cd5ac1..0000000 --- a/dist/urbm-2026.06.22.r001-x86_64-1.txz.sha256 +++ /dev/null @@ -1 +0,0 @@ -d7824cd92520205e3dafdd4681a4bcbe51f943f530785c8d8d4d7af88358a069 urbm-2026.06.22.r001-x86_64-1.txz diff --git a/dist/urbm-2026.06.22.r001-x86_64-1.txz b/dist/urbm-2026.06.22.r002-x86_64-1.txz similarity index 56% rename from dist/urbm-2026.06.22.r001-x86_64-1.txz rename to dist/urbm-2026.06.22.r002-x86_64-1.txz index 77a7f93..1b1b38f 100644 Binary files a/dist/urbm-2026.06.22.r001-x86_64-1.txz and b/dist/urbm-2026.06.22.r002-x86_64-1.txz differ diff --git a/dist/urbm-2026.06.22.r002-x86_64-1.txz.sha256 b/dist/urbm-2026.06.22.r002-x86_64-1.txz.sha256 new file mode 100644 index 0000000..b3830c6 --- /dev/null +++ b/dist/urbm-2026.06.22.r002-x86_64-1.txz.sha256 @@ -0,0 +1 @@ +1fda0986918fa5838cbd8f56ce013e1c5a2add51c41fb5cee14fe77c0e33c0b4 urbm-2026.06.22.r002-x86_64-1.txz diff --git a/dist/urbm.plg b/dist/urbm.plg index 4959e76..4c2f9c4 100644 --- a/dist/urbm.plg +++ b/dist/urbm.plg @@ -2,13 +2,17 @@ - + - + ]> +### 2026.06.22.r002 +- Preserve page and tree-container scroll positions after expanding deeply nested folders in source, target, settings, repository, and snapshot trees. +- Restore scroll positions after the browser has processed focus/layout changes to prevent jumps back to the root. + ### 2026.06.22.r001 - Prevent Unraid's global button styles from stretching folder tree disclosure controls over folder names. - Reserve fixed columns for tree arrows and checkboxes so folder names no longer overlap with controls. diff --git a/plugin/urbm.plg b/plugin/urbm.plg index 16fccf9..75b56ae 100644 --- a/plugin/urbm.plg +++ b/plugin/urbm.plg @@ -2,13 +2,17 @@ - + ]> +### 2026.06.22.r002 +- Preserve page and tree-container scroll positions after expanding deeply nested folders in source, target, settings, repository, and snapshot trees. +- Restore scroll positions after the browser has processed focus/layout changes to prevent jumps back to the root. + ### 2026.06.22.r001 - Prevent Unraid's global button styles from stretching folder tree disclosure controls over folder names. - Reserve fixed columns for tree arrows and checkboxes so folder names no longer overlap with controls. diff --git a/webgui/URBM.page b/webgui/URBM.page index 18f0596..31d3ed9 100644 --- a/webgui/URBM.page +++ b/webgui/URBM.page @@ -9,12 +9,12 @@ Tag="URBM Unraid Restic Backup Manager backup snapshots restore" - +
- -

URBM 2026.06.22.r001

Restic Backup Manager für Unraid

+ +

URBM 2026.06.22.r002

Restic Backup Manager für Unraid

Verbindung wird hergestellt...
@@ -32,4 +32,4 @@ $pluginRoot = '/plugins/urbm';
- + diff --git a/webgui/assets/urbm.js b/webgui/assets/urbm.js index e05ae67..367f753 100644 --- a/webgui/assets/urbm.js +++ b/webgui/assets/urbm.js @@ -375,6 +375,28 @@ } } + function captureScrollState(root=document) { + const trees=[...root.querySelectorAll('.bu-tree')].map((tree,index)=>({index,top:tree.scrollTop,left:tree.scrollLeft})); + return {windowX:window.scrollX,windowY:window.scrollY,trees}; + } + + function restoreScrollState(snapshot, root=document) { + if(!snapshot) return; + const apply=()=>{ + const trees=[...root.querySelectorAll('.bu-tree')]; + snapshot.trees.forEach(item=>{ + const tree=trees[item.index]; + if(tree) { + tree.scrollTop=item.top; + tree.scrollLeft=item.left; + } + }); + window.scrollTo(snapshot.windowX, snapshot.windowY); + }; + apply(); + requestAnimationFrame(()=>{ apply(); setTimeout(apply, 0); }); + } + function renderDashboard() { const active = state.runs.filter(r => ['queued','running','paused'].includes(r.status)).length; const orderedRuns = newestRuns(state.runs); @@ -517,15 +539,13 @@ function renderSourceBrowser() { const host=document.getElementById('bu-source-browser'); if(!host) return; - const windowX=window.scrollX, windowY=window.scrollY; - const previousScroll=host.querySelector('.bu-tree')?.scrollTop || 0; + const scrollSnapshot=captureScrollState(host); const chosen=state.backupSelections.length?state.backupSelections.map(path=>`${esc(path)}`).join(''):'Keine Ordner ausgewählt.'; const tree=state.sourceTreeRoots.map(item=>sourceTreeNode(item,0)).join(''); host.innerHTML=`

Backup-Quellen auswählen

Ordner mit dem Dreieck aufklappen. Unterordner werden erst bei Bedarf geladen.
Ausgewählt: ${state.backupSelections.length}
${chosen}
${tree||'
Keine Ordner verfügbar.
'}
`; - const treeElement=host.querySelector('.bu-tree'); if(treeElement) treeElement.scrollTop=previousScroll; host.querySelectorAll('.bu-source-select[data-partial="true"]').forEach(box=>{box.indeterminate=true;box.setAttribute('aria-checked','mixed');}); enhanceTooltips(host); - window.scrollTo(windowX, windowY); + restoreScrollState(scrollSnapshot, host); } async function toggleSourceTree(path, forceOpen=false) { @@ -569,13 +589,11 @@ function renderDirectoryTree(key) { const tree=state.directoryTrees[key]; const host=document.getElementById(`bu-dir-tree-${key}`); if(!tree||!host) return; - const windowX=window.scrollX, windowY=window.scrollY; - const previousScroll=host.querySelector('.bu-tree')?.scrollTop || 0; + const scrollSnapshot=captureScrollState(host); const html=tree.roots.map(item=>directoryTreeNode(key,item,0)).join(''); host.innerHTML=`

${esc(tree.title)}

Ordner mit dem Dreieck aufklappen und den gewünschten Zielordner auswählen.
${html||'
Keine Ordner verfügbar.
'}
`; - const treeElement=host.querySelector('.bu-tree'); if(treeElement) treeElement.scrollTop=previousScroll; enhanceTooltips(host); - window.scrollTo(windowX, windowY); + restoreScrollState(scrollSnapshot, host); } async function toggleDirectoryTree(key, path, forceOpen=false) { @@ -755,9 +773,9 @@ return state.config; } function renderPreservingScroll() { - const windowX=window.scrollX, windowY=window.scrollY; + const scrollSnapshot=captureScrollState(content); render(); - window.scrollTo(windowX, windowY); + restoreScrollState(scrollSnapshot, content); } const lines = (value) => value.split('\n').map(v=>v.trim()).filter(Boolean); const runTime = run => new Date(run.createdAt || run.startedAt || run.finishedAt || 0).getTime() || 0;