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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vjs6n29WzQx8pGg3KJPGXk
(cherry picked from commit 7be969ea7b2a9b7441a8c5a65cc8a0928009d595)
This commit is contained in:
Claude
2026-07-30 19:53:13 +00:00
committed by Acentech Dev
parent f5b4292af1
commit 79922204c5
3 changed files with 144 additions and 118 deletions

View File

@@ -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<LogFiles> Logger
<PageTitle>OpenMU: @Resources.LogFiles</PageTitle>
<Breadcrumb IsFirstFromRoot="true" Caption="@Resources.LogFiles"/>
@@ -103,7 +105,7 @@
</div>
</div>
<div id="log-terminal" class="p-3 rounded" style="height: 480px; overflow-y: auto; font-family: 'Consolas', 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; line-height: 1.5; white-space: pre-wrap; background-color: #0c0c0d !important; border: 1px solid #2d2d30;">
<div id="@TerminalElementId" class="p-3 rounded" style="height: 480px; overflow-y: auto; font-family: 'Consolas', 'Liberation Mono', Menlo, Courier, monospace; font-size: 13px; line-height: 1.5; white-space: pre-wrap; background-color: #0c0c0d !important; border: 1px solid #2d2d30;">
@if (this._logLines.Count == 0)
{
<div class="text-muted text-center py-5">@Resources.NoLogEntriesFound</div>
@@ -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<FileInfo> _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<IJSObjectReference>("import", "./_content/MUnique.OpenMU.Web.AdminPanel/Pages/LogFiles.razor.js");
this._jsModule = await this.JSRuntime.InvokeAsync<IJSObjectReference>("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)
{
return;
}
try
{
await this._jsModule.InvokeVoidAsync("scrollToBottom", "log-terminal");
await this._jsModule.InvokeVoidAsync("scrollToBottom", TerminalElementId);
}
catch
catch (JSDisconnectedException)
{
// Ignore JS call errors
// The circuit is gone; nothing to do.
}
}
private async ValueTask<bool> IsScrolledToBottomAsync()
{
if (this._jsModule is null)
{
return true;
}
try
{
return await this._jsModule.InvokeAsync<bool>("isScrolledToBottom", TerminalElementId);
}
catch (JSDisconnectedException)
{
return false;
}
}
}

View File

@@ -1,4 +1,4 @@
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
@@ -249,6 +249,15 @@ namespace MUnique.OpenMU.Web.AdminPanel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Close.
/// </summary>
public static string Close {
get {
return ResourceManager.GetString("Close", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Command.
/// </summary>
@@ -468,6 +477,15 @@ namespace MUnique.OpenMU.Web.AdminPanel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Download File.
/// </summary>
public static string DownloadFile {
get {
return ResourceManager.GetString("DownloadFile", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Drop item groups.
/// </summary>
@@ -558,6 +576,15 @@ namespace MUnique.OpenMU.Web.AdminPanel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Filter log entries....
/// </summary>
public static string FilterLogEntries {
get {
return ResourceManager.GetString("FilterLogEntries", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Finished! Have fun :).
/// </summary>
@@ -774,6 +801,15 @@ namespace MUnique.OpenMU.Web.AdminPanel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Live.
/// </summary>
public static string Live {
get {
return ResourceManager.GetString("Live", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Live Map.
/// </summary>
@@ -819,6 +855,15 @@ namespace MUnique.OpenMU.Web.AdminPanel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Log Viewer.
/// </summary>
public static string LogViewer {
get {
return ResourceManager.GetString("LogViewer", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Major.
/// </summary>
@@ -972,6 +1017,24 @@ namespace MUnique.OpenMU.Web.AdminPanel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to No log entries found..
/// </summary>
public static string NoLogEntriesFound {
get {
return ResourceManager.GetString("NoLogEntriesFound", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No log entries match your filter..
/// </summary>
public static string NoLogEntriesMatchFilter {
get {
return ResourceManager.GetString("NoLogEntriesMatchFilter", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to This command has no parameters..
/// </summary>
@@ -1197,6 +1260,15 @@ namespace MUnique.OpenMU.Web.AdminPanel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Reload File List.
/// </summary>
public static string ReloadFileList {
get {
return ResourceManager.GetString("ReloadFileList", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Remove.
/// </summary>
@@ -1260,6 +1332,15 @@ namespace MUnique.OpenMU.Web.AdminPanel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Scroll to Bottom.
/// </summary>
public static string ScrollToBottom {
get {
return ResourceManager.GetString("ScrollToBottom", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Search.
/// </summary>
@@ -1377,6 +1458,15 @@ namespace MUnique.OpenMU.Web.AdminPanel.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Showing {0} of {1} lines (Last {2} lines loaded)..
/// </summary>
public static string ShowingXOfYLines {
get {
return ResourceManager.GetString("ShowingXOfYLines", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Size.
/// </summary>
@@ -1646,95 +1736,5 @@ namespace MUnique.OpenMU.Web.AdminPanel.Properties {
return ResourceManager.GetString("YesCreateTestAccounts", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Log Viewer.
/// </summary>
public static string LogViewer {
get {
return ResourceManager.GetString("LogViewer", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Live.
/// </summary>
public static string Live {
get {
return ResourceManager.GetString("Live", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Close.
/// </summary>
public static string Close {
get {
return ResourceManager.GetString("Close", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Filter log entries....
/// </summary>
public static string FilterLogEntries {
get {
return ResourceManager.GetString("FilterLogEntries", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No log entries found..
/// </summary>
public static string NoLogEntriesFound {
get {
return ResourceManager.GetString("NoLogEntriesFound", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No log entries match your filter..
/// </summary>
public static string NoLogEntriesMatchFilter {
get {
return ResourceManager.GetString("NoLogEntriesMatchFilter", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Showing {0} of {1} lines (Last {2} lines loaded)..
/// </summary>
public static string ShowingXOfYLines {
get {
return ResourceManager.GetString("ShowingXOfYLines", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Scroll to Bottom.
/// </summary>
public static string ScrollToBottom {
get {
return ResourceManager.GetString("ScrollToBottom", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Reload File List.
/// </summary>
public static string ReloadFileList {
get {
return ResourceManager.GetString("ReloadFileList", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Download File.
/// </summary>
public static string DownloadFile {
get {
return ResourceManager.GetString("DownloadFile", resourceCulture);
}
}
}
}

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@@ -612,18 +612,12 @@
<data name="Target" xml:space="preserve">
<value>Target</value>
</data>
<data name="Actions" xml:space="preserve">
<value>Actions</value>
</data>
<data name="LogViewer" xml:space="preserve">
<value>Log Viewer</value>
</data>
<data name="Live" xml:space="preserve">
<value>Live</value>
</data>
<data name="Refresh" xml:space="preserve">
<value>Refresh</value>
</data>
<data name="Close" xml:space="preserve">
<value>Close</value>
</data>