From 79922204c556b6ce31652dab3e42b1e765dc08df Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 19:53:13 +0000 Subject: [PATCH] Fix build and behavior issues in the admin panel log viewer - Resources.resx: close the unterminated DownloadFile data element, which made the file invalid XML, and remove the duplicated Actions and Refresh entries which already exist. - Resources.Designer.cs: restore the UTF-8 BOM and put the new properties into the alphabetical order the strongly typed resource builder produces, so the file matches its generated form again. - LogFiles.razor: import the collocated script from ./Pages/LogFiles.razor.js. The _content/{PackageId} prefix only applies to razor class libraries, so the import failed for this web application and the module was never loaded. - LogFiles.razor: follow the new entries in live mode again by using the isScrolledToBottom helper, so the terminal scrolls along unless the user scrolled up to read the history. - LogFiles.razor: only catch the expected javascript interop exceptions and log a failing module import instead of swallowing it silently. - LogFiles.razor: restore the BOM and the trailing newline. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Vjs6n29WzQx8pGg3KJPGXk (cherry picked from commit 7be969ea7b2a9b7441a8c5a65cc8a0928009d595) --- src/Web/AdminPanel/Pages/LogFiles.razor | 72 +++++-- .../Properties/Resources.Designer.cs | 182 +++++++++--------- src/Web/AdminPanel/Properties/Resources.resx | 8 +- 3 files changed, 144 insertions(+), 118 deletions(-) diff --git a/src/Web/AdminPanel/Pages/LogFiles.razor b/src/Web/AdminPanel/Pages/LogFiles.razor index c79ef4b..49ecc4a 100644 --- a/src/Web/AdminPanel/Pages/LogFiles.razor +++ b/src/Web/AdminPanel/Pages/LogFiles.razor @@ -1,9 +1,11 @@ @page "/logfiles" @using System.IO +@using Microsoft.Extensions.Logging @using MUnique.OpenMU.Web.AdminPanel.Properties @implements IAsyncDisposable @inject IJSRuntime JSRuntime +@inject ILogger Logger OpenMU: @Resources.LogFiles @@ -103,7 +105,7 @@ -
+
@if (this._logLines.Count == 0) {
@Resources.NoLogEntriesFound
@@ -139,6 +141,7 @@ private const int MaxLogLinesToRead = 300; private const long LogReadBufferSizeBytes = 102400; // 100 KB private const int LiveUpdateIntervalMs = 2000; + private const string TerminalElementId = "log-terminal"; private readonly List _files = new(); private FileInfo? _selectedFile; @@ -164,9 +167,9 @@ { await this._jsModule.DisposeAsync(); } - catch + catch (JSDisconnectedException) { - // Ignore JS module disposal errors + // The circuit is already gone, so the module is disposed anyway. } } } @@ -184,11 +187,16 @@ { try { - this._jsModule = await this.JSRuntime.InvokeAsync("import", "./_content/MUnique.OpenMU.Web.AdminPanel/Pages/LogFiles.razor.js"); + this._jsModule = await this.JSRuntime.InvokeAsync("import", "./Pages/LogFiles.razor.js"); } - catch + catch (JSException ex) { - // Fallback gracefully if JS module import fails + // Without the module, the viewer still works - only the automatic scrolling is unavailable. + this.Logger.LogWarning(ex, "Could not load the log viewer javascript module."); + } + catch (JSDisconnectedException) + { + // The circuit is gone; nothing to do. } } @@ -333,7 +341,7 @@ return; } - this.InvokeAsync(() => + this.InvokeAsync(async () => { if (this._disposed || this._selectedFile == null) { @@ -341,11 +349,16 @@ } var updatedInfo = new FileInfo(this._selectedFile.FullName); - if (updatedInfo.Length != this._lastFileLength || updatedInfo.LastWriteTimeUtc != this._lastFileWriteTime) + if (updatedInfo.Length == this._lastFileLength && updatedInfo.LastWriteTimeUtc == this._lastFileWriteTime) { - this.RefreshLogLines(); - this.StateHasChanged(); + return; } + + // Only follow the new entries when the user didn't scroll up to read the history. + var isFollowing = await this.IsScrolledToBottomAsync(); + this.RefreshLogLines(); + this._shouldScrollToBottom = isFollowing; + this.StateHasChanged(); }); }, null, 0, LiveUpdateIntervalMs); } @@ -387,16 +400,35 @@ private async Task ScrollToBottomAsync() { - if (this._jsModule != null) + if (this._jsModule is null) { - try - { - await this._jsModule.InvokeVoidAsync("scrollToBottom", "log-terminal"); - } - catch - { - // Ignore JS call errors - } + return; + } + + try + { + await this._jsModule.InvokeVoidAsync("scrollToBottom", TerminalElementId); + } + catch (JSDisconnectedException) + { + // The circuit is gone; nothing to do. } } -} \ No newline at end of file + + private async ValueTask IsScrolledToBottomAsync() + { + if (this._jsModule is null) + { + return true; + } + + try + { + return await this._jsModule.InvokeAsync("isScrolledToBottom", TerminalElementId); + } + catch (JSDisconnectedException) + { + return false; + } + } +} diff --git a/src/Web/AdminPanel/Properties/Resources.Designer.cs b/src/Web/AdminPanel/Properties/Resources.Designer.cs index 0310f80..1b469d4 100644 --- a/src/Web/AdminPanel/Properties/Resources.Designer.cs +++ b/src/Web/AdminPanel/Properties/Resources.Designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 @@ -249,6 +249,15 @@ namespace MUnique.OpenMU.Web.AdminPanel.Properties { } } + /// + /// Looks up a localized string similar to Close. + /// + public static string Close { + get { + return ResourceManager.GetString("Close", resourceCulture); + } + } + /// /// Looks up a localized string similar to Command. /// @@ -468,6 +477,15 @@ namespace MUnique.OpenMU.Web.AdminPanel.Properties { } } + /// + /// Looks up a localized string similar to Download File. + /// + public static string DownloadFile { + get { + return ResourceManager.GetString("DownloadFile", resourceCulture); + } + } + /// /// Looks up a localized string similar to Drop item groups. /// @@ -558,6 +576,15 @@ namespace MUnique.OpenMU.Web.AdminPanel.Properties { } } + /// + /// Looks up a localized string similar to Filter log entries.... + /// + public static string FilterLogEntries { + get { + return ResourceManager.GetString("FilterLogEntries", resourceCulture); + } + } + /// /// Looks up a localized string similar to Finished! Have fun :). /// @@ -774,6 +801,15 @@ namespace MUnique.OpenMU.Web.AdminPanel.Properties { } } + /// + /// Looks up a localized string similar to Live. + /// + public static string Live { + get { + return ResourceManager.GetString("Live", resourceCulture); + } + } + /// /// Looks up a localized string similar to Live Map. /// @@ -819,6 +855,15 @@ namespace MUnique.OpenMU.Web.AdminPanel.Properties { } } + /// + /// Looks up a localized string similar to Log Viewer. + /// + public static string LogViewer { + get { + return ResourceManager.GetString("LogViewer", resourceCulture); + } + } + /// /// Looks up a localized string similar to Major. /// @@ -972,6 +1017,24 @@ namespace MUnique.OpenMU.Web.AdminPanel.Properties { } } + /// + /// Looks up a localized string similar to No log entries found.. + /// + public static string NoLogEntriesFound { + get { + return ResourceManager.GetString("NoLogEntriesFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No log entries match your filter.. + /// + public static string NoLogEntriesMatchFilter { + get { + return ResourceManager.GetString("NoLogEntriesMatchFilter", resourceCulture); + } + } + /// /// Looks up a localized string similar to This command has no parameters.. /// @@ -1197,6 +1260,15 @@ namespace MUnique.OpenMU.Web.AdminPanel.Properties { } } + /// + /// Looks up a localized string similar to Reload File List. + /// + public static string ReloadFileList { + get { + return ResourceManager.GetString("ReloadFileList", resourceCulture); + } + } + /// /// Looks up a localized string similar to Remove. /// @@ -1260,6 +1332,15 @@ namespace MUnique.OpenMU.Web.AdminPanel.Properties { } } + /// + /// Looks up a localized string similar to Scroll to Bottom. + /// + public static string ScrollToBottom { + get { + return ResourceManager.GetString("ScrollToBottom", resourceCulture); + } + } + /// /// Looks up a localized string similar to Search. /// @@ -1377,6 +1458,15 @@ namespace MUnique.OpenMU.Web.AdminPanel.Properties { } } + /// + /// Looks up a localized string similar to Showing {0} of {1} lines (Last {2} lines loaded).. + /// + public static string ShowingXOfYLines { + get { + return ResourceManager.GetString("ShowingXOfYLines", resourceCulture); + } + } + /// /// Looks up a localized string similar to Size. /// @@ -1646,95 +1736,5 @@ namespace MUnique.OpenMU.Web.AdminPanel.Properties { return ResourceManager.GetString("YesCreateTestAccounts", resourceCulture); } } - - /// - /// Looks up a localized string similar to Log Viewer. - /// - public static string LogViewer { - get { - return ResourceManager.GetString("LogViewer", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Live. - /// - public static string Live { - get { - return ResourceManager.GetString("Live", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Close. - /// - public static string Close { - get { - return ResourceManager.GetString("Close", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Filter log entries.... - /// - public static string FilterLogEntries { - get { - return ResourceManager.GetString("FilterLogEntries", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to No log entries found.. - /// - public static string NoLogEntriesFound { - get { - return ResourceManager.GetString("NoLogEntriesFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to No log entries match your filter.. - /// - public static string NoLogEntriesMatchFilter { - get { - return ResourceManager.GetString("NoLogEntriesMatchFilter", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Showing {0} of {1} lines (Last {2} lines loaded).. - /// - public static string ShowingXOfYLines { - get { - return ResourceManager.GetString("ShowingXOfYLines", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Scroll to Bottom. - /// - public static string ScrollToBottom { - get { - return ResourceManager.GetString("ScrollToBottom", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Reload File List. - /// - public static string ReloadFileList { - get { - return ResourceManager.GetString("ReloadFileList", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Download File. - /// - public static string DownloadFile { - get { - return ResourceManager.GetString("DownloadFile", resourceCulture); - } - } } } diff --git a/src/Web/AdminPanel/Properties/Resources.resx b/src/Web/AdminPanel/Properties/Resources.resx index 50a2350..06f87cc 100644 --- a/src/Web/AdminPanel/Properties/Resources.resx +++ b/src/Web/AdminPanel/Properties/Resources.resx @@ -1,4 +1,4 @@ - +