diff options
| -rw-r--r-- | Jellyfin.Server/ServerSetupApp/index.mstemplate.html | 134 |
1 files changed, 119 insertions, 15 deletions
diff --git a/Jellyfin.Server/ServerSetupApp/index.mstemplate.html b/Jellyfin.Server/ServerSetupApp/index.mstemplate.html index 38cb5cea9e..b523dc6276 100644 --- a/Jellyfin.Server/ServerSetupApp/index.mstemplate.html +++ b/Jellyfin.Server/ServerSetupApp/index.mstemplate.html @@ -41,7 +41,8 @@ background-color: var(--jf-bg); background-image: radial-gradient(circle at 50% -10%, var(--jf-bg-accent), var(--jf-bg) 60%); color: var(--jf-text); - padding: 2.5rem 1rem 4rem; + /* Bottom padding doubles as the minimum gap the log viewport keeps from the window bottom. */ + padding: 2.5rem 1rem 50px; } .flex-row { @@ -162,6 +163,25 @@ text-decoration: underline; } + /* Bounded, scrollable log viewport that scales with the window height. */ + .logs-panel .logs-scroll { + max-height: 60vh; + overflow-y: auto; + overscroll-behavior: contain; + padding-right: 0.25rem; + scrollbar-width: thin; + scrollbar-color: var(--jf-border) transparent; + } + + .logs-panel .logs-scroll::-webkit-scrollbar { + width: 8px; + } + + .logs-panel .logs-scroll::-webkit-scrollbar-thumb { + background-color: var(--jf-border); + border-radius: 4px; + } + ol.action-list * { font-family: ui-monospace, "Cascadia Mono", "Segoe UI Mono", "Roboto Mono", Menlo, Consolas, monospace; font-weight: 400; @@ -314,7 +334,7 @@ <details open> <summary>{{DateOfCreation}} - {{Content}}</summary> <ul class="action-list"> - {{--| #EACH children.Reverse() |-}} + {{--| #EACH children |-}} {{#IMPORT 'LogEntry'}} {{--| /EACH |-}} </ul> @@ -332,22 +352,106 @@ <h2>Startup log</h2> <a class="download-logs" href='/startup/logger' target="_blank">Download full logs</a> </div> - <ol class="action-list"> - {{#FOREACH log IN logs.Reverse()}} - {{#IMPORT 'LogEntry' #WITH log}} - {{/FOREACH}} - </ol> + <div class="logs-scroll" id="logs-scroll"> + <ol class="action-list"> + {{#FOREACH log IN logs}} + {{#IMPORT 'LogEntry' #WITH log}} + {{/FOREACH}} + </ol> + </div> </div> {{/IF}} </div> -</body> + <script> + (function () { + var reporting = false; + {{#IF isInReportingMode}} + reporting = true; + {{/IF}} + var intervalMs = {{ retryValue.TotalMilliseconds }}; + + var box = document.getElementById('logs-scroll'); + var panel = box ? box.closest('.logs-panel') : null; + + function px(el, prop) { + return el ? (parseFloat(getComputedStyle(el)[prop]) || 0) : 0; + } -{{^IF isInReportingMode}} -<script> - setTimeout(() => { - window.location.reload(); - }, {{ retryValue.TotalMilliseconds }}); -</script> -{{/IF}} + // Size the log viewport so the panel never runs past the bottom of the window + // (keeping the body's bottom padding as the margin). Recomputed on resize. + function fit() { + if (!box) { + return; + } + var topFromDoc = box.getBoundingClientRect().top + window.scrollY; + var below = px(panel, 'paddingBottom') + px(document.body, 'paddingBottom'); + box.style.maxHeight = Math.max(window.innerHeight - topFromDoc - below, 120) + 'px'; + } + + function nearBottom(el) { + return el.scrollHeight - el.scrollTop - el.clientHeight < 24; + } + + fit(); + window.addEventListener('resize', fit); + window.addEventListener('load', fit); + if (box) { + box.scrollTop = box.scrollHeight; // start pinned to the newest entry + } + + // In the terminal error state the page is static, so stop here. + if (reporting) { + return; + } + + // Soft refresh: pull the page in the background and swap the log list + activity line in + // place, so polling never disturbs where the user has scrolled. Only follow to the bottom + // when the user is already there. A real reload happens only on the final transition. + function poll() { + fetch(window.location.href, { cache: 'no-store' }).then(function (resp) { + if (resp.ok) { + // The real server is now answering (HTTP 200) -> load the actual app. + window.location.reload(); + return null; + } + return resp.text(); + }).then(function (html) { + if (!html) { + return; + } + var doc = new DOMParser().parseFromString(html, 'text/html'); + + // Startup failed and the page switched to the error view -> reload to render it. + if (doc.querySelector('.status-card.is-error')) { + window.location.reload(); + return; + } + + var newStatus = doc.querySelector('.status-text'); + var curStatus = document.querySelector('.status-text'); + if (newStatus && curStatus) { + curStatus.innerHTML = newStatus.innerHTML; + } + + if (box) { + var newList = doc.querySelector('#logs-scroll .action-list'); + var curList = box.querySelector('.action-list'); + if (newList && curList) { + var stick = nearBottom(box); + var prevTop = box.scrollTop; + curList.replaceWith(document.importNode(newList, true)); + fit(); + box.scrollTop = stick ? box.scrollHeight : prevTop; + } + } + }).catch(function () { + // Server is mid-transition (port rebinding); just try again on the next tick. + }); + } + + setInterval(poll, intervalMs); + })(); + </script> +</body> </html> |
