diff --git a/app/MindWork AI Studio/Chat/ContentBlockComponent.razor b/app/MindWork AI Studio/Chat/ContentBlockComponent.razor
index 7c09ae78..f3f2f528 100644
--- a/app/MindWork AI Studio/Chat/ContentBlockComponent.razor
+++ b/app/MindWork AI Studio/Chat/ContentBlockComponent.razor
@@ -96,10 +96,10 @@
}
else
{
-
+
@if (textContent.Sources.Count > 0)
{
-
+
}
}
}
@@ -135,4 +135,4 @@
}
}
-
\ No newline at end of file
+
diff --git a/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs b/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs
index e29a016d..17894232 100644
--- a/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs
+++ b/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs
@@ -10,6 +10,18 @@ namespace AIStudio.Chat;
///
public partial class ContentBlockComponent : MSGComponentBase
{
+ private static readonly string[] HTML_TAG_MARKERS =
+ [
+ "
/// The role of the chat content block.
///
@@ -68,18 +80,36 @@ public partial class ContentBlockComponent : MSGComponentBase
private RustService RustService { get; init; } = null!;
private bool HideContent { get; set; }
+ private bool hasRenderHash;
+ private int lastRenderHash;
#region Overrides of ComponentBase
protected override async Task OnInitializedAsync()
{
- // Register the streaming events:
- this.Content.StreamingDone = this.AfterStreaming;
- this.Content.StreamingEvent = () => this.InvokeAsync(this.StateHasChanged);
-
+ this.RegisterStreamingEvents();
await base.OnInitializedAsync();
}
+ protected override Task OnParametersSetAsync()
+ {
+ this.RegisterStreamingEvents();
+ return base.OnParametersSetAsync();
+ }
+
+ protected override bool ShouldRender()
+ {
+ var currentRenderHash = this.CreateRenderHash();
+ if (!this.hasRenderHash || currentRenderHash != this.lastRenderHash)
+ {
+ this.lastRenderHash = currentRenderHash;
+ this.hasRenderHash = true;
+ return true;
+ }
+
+ return false;
+ }
+
///
/// Gets called when the content stream ended.
///
@@ -111,6 +141,47 @@ public partial class ContentBlockComponent : MSGComponentBase
});
}
+ private void RegisterStreamingEvents()
+ {
+ this.Content.StreamingDone = this.AfterStreaming;
+ this.Content.StreamingEvent = () => this.InvokeAsync(this.StateHasChanged);
+ }
+
+ private int CreateRenderHash()
+ {
+ var hash = new HashCode();
+ hash.Add(this.Role);
+ hash.Add(this.Type);
+ hash.Add(this.Time);
+ hash.Add(this.Class);
+ hash.Add(this.IsLastContentBlock);
+ hash.Add(this.IsSecondToLastBlock);
+ hash.Add(this.HideContent);
+ hash.Add(this.SettingsManager.IsDarkMode);
+ hash.Add(this.RegenerateEnabled());
+ hash.Add(this.Content.InitialRemoteWait);
+ hash.Add(this.Content.IsStreaming);
+ hash.Add(this.Content.FileAttachments.Count);
+ hash.Add(this.Content.Sources.Count);
+
+ switch (this.Content)
+ {
+ case ContentText text:
+ var textValue = text.Text ?? string.Empty;
+ hash.Add(textValue.Length);
+ hash.Add(textValue.GetHashCode(StringComparison.Ordinal));
+ hash.Add(text.Sources.Count);
+ break;
+
+ case ContentImage image:
+ hash.Add(image.SourceType);
+ hash.Add(image.Source);
+ break;
+ }
+
+ return hash.ToHashCode();
+ }
+
#endregion
private string CardClasses => $"my-2 rounded-lg {this.Class}";
@@ -121,6 +192,34 @@ public partial class ContentBlockComponent : MSGComponentBase
{
CodeBlock = { Theme = this.CodeColorPalette },
};
+
+ private string NormalizeMarkdownForRendering(string text)
+ {
+ var cleaned = text.RemoveThinkTags().Trim();
+ if (string.IsNullOrWhiteSpace(cleaned))
+ return string.Empty;
+
+ if (cleaned.Contains("```", StringComparison.Ordinal))
+ return cleaned;
+
+ if (LooksLikeRawHtml(cleaned))
+ return $"```html{Environment.NewLine}{cleaned}{Environment.NewLine}```";
+
+ return cleaned;
+ }
+
+ private static bool LooksLikeRawHtml(string text)
+ {
+ var content = text.TrimStart();
+ if (!content.StartsWith("<", StringComparison.Ordinal))
+ return false;
+
+ foreach (var marker in HTML_TAG_MARKERS)
+ if (content.Contains(marker, StringComparison.OrdinalIgnoreCase))
+ return true;
+
+ return content.Contains("", StringComparison.Ordinal) || content.Contains("/>", StringComparison.Ordinal);
+ }
private async Task RemoveBlock()
{
@@ -194,4 +293,4 @@ public partial class ContentBlockComponent : MSGComponentBase
var result = await ReviewAttachmentsDialog.OpenDialogAsync(this.DialogService, this.Content.FileAttachments.ToHashSet());
this.Content.FileAttachments = result.ToList();
}
-}
\ No newline at end of file
+}
diff --git a/app/MindWork AI Studio/Components/Changelog.razor b/app/MindWork AI Studio/Components/Changelog.razor
index 1afebfc3..2d573afb 100644
--- a/app/MindWork AI Studio/Components/Changelog.razor
+++ b/app/MindWork AI Studio/Components/Changelog.razor
@@ -6,4 +6,4 @@
}
-
\ No newline at end of file
+
diff --git a/app/MindWork AI Studio/Components/ChatComponent.razor b/app/MindWork AI Studio/Components/ChatComponent.razor
index 52b82b9b..1467c01e 100644
--- a/app/MindWork AI Studio/Components/ChatComponent.razor
+++ b/app/MindWork AI Studio/Components/ChatComponent.razor
@@ -16,6 +16,7 @@
@if (!block.HideFromUser)
{
-
\ No newline at end of file
+
diff --git a/app/MindWork AI Studio/Components/ConfidenceInfo.razor b/app/MindWork AI Studio/Components/ConfidenceInfo.razor
index f27fe58e..ed7c45ef 100644
--- a/app/MindWork AI Studio/Components/ConfidenceInfo.razor
+++ b/app/MindWork AI Studio/Components/ConfidenceInfo.razor
@@ -28,7 +28,7 @@
@T("Description")
-
+
@if (this.currentConfidence.Sources.Count > 0)
{
@@ -67,4 +67,4 @@
-
\ No newline at end of file
+
diff --git a/app/MindWork AI Studio/Components/Settings/SettingsPanelProviders.razor b/app/MindWork AI Studio/Components/Settings/SettingsPanelProviders.razor
index f6704dc5..f89d7b52 100644
--- a/app/MindWork AI Studio/Components/Settings/SettingsPanelProviders.razor
+++ b/app/MindWork AI Studio/Components/Settings/SettingsPanelProviders.razor
@@ -104,7 +104,7 @@
@context.ToName()
-
+
diff --git a/app/MindWork AI Studio/Dialogs/DocumentCheckDialog.razor b/app/MindWork AI Studio/Dialogs/DocumentCheckDialog.razor
index f3b75837..dc2ee563 100644
--- a/app/MindWork AI Studio/Dialogs/DocumentCheckDialog.razor
+++ b/app/MindWork AI Studio/Dialogs/DocumentCheckDialog.razor
@@ -54,7 +54,7 @@
Class="ma-2 pe-4"
HelperText="@T("This is the content we loaded from your file — including headings, lists, and formatting. Use this to verify your file loads as expected.")">
-
+
@@ -83,4 +83,4 @@
@T("Close")
-
\ No newline at end of file
+
diff --git a/app/MindWork AI Studio/Dialogs/PandocDialog.razor b/app/MindWork AI Studio/Dialogs/PandocDialog.razor
index 2914b38e..f9638ff9 100644
--- a/app/MindWork AI Studio/Dialogs/PandocDialog.razor
+++ b/app/MindWork AI Studio/Dialogs/PandocDialog.razor
@@ -30,7 +30,7 @@
}
else if (!string.IsNullOrWhiteSpace(this.licenseText))
{
-
+
}
@@ -226,4 +226,4 @@
}
}
-
\ No newline at end of file
+
diff --git a/app/MindWork AI Studio/Dialogs/UpdateDialog.razor b/app/MindWork AI Studio/Dialogs/UpdateDialog.razor
index 62f3dd7a..3eaa6db9 100644
--- a/app/MindWork AI Studio/Dialogs/UpdateDialog.razor
+++ b/app/MindWork AI Studio/Dialogs/UpdateDialog.razor
@@ -5,7 +5,7 @@
@this.HeaderText
-
+
@@ -15,4 +15,4 @@
@T("Install now")
-
\ No newline at end of file
+
diff --git a/app/MindWork AI Studio/Pages/Home.razor b/app/MindWork AI Studio/Pages/Home.razor
index 53d48e6e..43118b24 100644
--- a/app/MindWork AI Studio/Pages/Home.razor
+++ b/app/MindWork AI Studio/Pages/Home.razor
@@ -27,7 +27,7 @@
-
+
@@ -35,9 +35,9 @@
-
+
-
\ No newline at end of file
+
diff --git a/app/MindWork AI Studio/Pages/Information.razor b/app/MindWork AI Studio/Pages/Information.razor
index 435a6a56..e9389237 100644
--- a/app/MindWork AI Studio/Pages/Information.razor
+++ b/app/MindWork AI Studio/Pages/Information.razor
@@ -297,7 +297,7 @@
-
+
diff --git a/app/MindWork AI Studio/Tools/Markdown.cs b/app/MindWork AI Studio/Tools/Markdown.cs
index 0ecf3774..24946386 100644
--- a/app/MindWork AI Studio/Tools/Markdown.cs
+++ b/app/MindWork AI Studio/Tools/Markdown.cs
@@ -1,7 +1,16 @@
+using Markdig;
+
namespace AIStudio.Tools;
public static class Markdown
{
+ private static readonly MarkdownPipeline SAFE_MARKDOWN_PIPELINE = new MarkdownPipelineBuilder()
+ .UseAdvancedExtensions()
+ .DisableHtml()
+ .Build();
+
+ public static MarkdownPipeline SecurePipeline => SAFE_MARKDOWN_PIPELINE;
+
public static MudMarkdownProps DefaultConfig => new()
{
Heading =
@@ -19,4 +28,4 @@ public static class Markdown
},
}
};
-}
\ No newline at end of file
+}
diff --git a/app/MindWork AI Studio/wwwroot/changelog/v26.3.1.md b/app/MindWork AI Studio/wwwroot/changelog/v26.3.1.md
index 840e2947..7f4e3b46 100644
--- a/app/MindWork AI Studio/wwwroot/changelog/v26.3.1.md
+++ b/app/MindWork AI Studio/wwwroot/changelog/v26.3.1.md
@@ -2,4 +2,5 @@
- Improved the performance by caching the OS language detection and requesting the user language only once per app start.
- Improved the user-language logging by limiting language detection logs to a single entry per app start.
- Improved the logbook readability by removing non-readable special characters from log entries.
-- Improved the logbook reliability by significantly reducing duplicate log entries.
\ No newline at end of file
+- Improved the logbook reliability by significantly reducing duplicate log entries.
+- Fixed an issue where the app could turn white or appear invisible in certain chats after HTML-like content was shown.
\ No newline at end of file