diff --git a/app/MindWork AI Studio/Chat/ContentBlockComponent.razor b/app/MindWork AI Studio/Chat/ContentBlockComponent.razor
index 579e8bf2..17158e45 100644
--- a/app/MindWork AI Studio/Chat/ContentBlockComponent.razor
+++ b/app/MindWork AI Studio/Chat/ContentBlockComponent.razor
@@ -96,7 +96,18 @@
}
else
{
-
+ var renderSegments = GetMarkdownRenderSegments(textContent.Text);
+ foreach (var segment in renderSegments)
+ {
+ if (segment.Type is MarkdownRenderSegmentType.MARKDOWN)
+ {
+
+ }
+ else
+ {
+
+ }
+ }
@if (textContent.Sources.Count > 0)
{
diff --git a/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs b/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs
index 29e70487..f2036774 100644
--- a/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs
+++ b/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs
@@ -2,6 +2,7 @@ using AIStudio.Components;
using AIStudio.Dialogs;
using AIStudio.Tools.Services;
using Microsoft.AspNetCore.Components;
+using System.Text;
namespace AIStudio.Chat;
@@ -194,6 +195,72 @@ public partial class ContentBlockComponent : MSGComponentBase
CodeBlock = { Theme = this.CodeColorPalette },
};
+ private static IReadOnlyList GetMarkdownRenderSegments(string text)
+ {
+ var normalized = NormalizeMarkdownForRendering(text);
+ if (string.IsNullOrWhiteSpace(normalized))
+ return [];
+
+ var normalizedWithUnixLineEndings = normalized.Replace("\r\n", "\n", StringComparison.Ordinal).Replace('\r', '\n');
+ var lines = normalizedWithUnixLineEndings.Split('\n');
+ var markdownBuilder = new StringBuilder();
+ var mathBuilder = new StringBuilder();
+ var segments = new List();
+ string? activeCodeFenceMarker = null;
+ var inMathBlock = false;
+
+ foreach (var line in lines)
+ {
+ var trimmedLine = line.Trim();
+
+ if (!inMathBlock && TryUpdateCodeFenceState(trimmedLine, ref activeCodeFenceMarker))
+ {
+ AppendLine(markdownBuilder, line);
+ continue;
+ }
+
+ if (activeCodeFenceMarker is not null)
+ {
+ AppendLine(markdownBuilder, line);
+ continue;
+ }
+
+ if (trimmedLine == "$$")
+ {
+ if (inMathBlock)
+ {
+ segments.Add(new(MarkdownRenderSegmentType.MATH_BLOCK, mathBuilder.ToString().Trim('\r', '\n')));
+ mathBuilder.Clear();
+ inMathBlock = false;
+ }
+ else
+ {
+ FlushMarkdownSegment();
+ inMathBlock = true;
+ }
+
+ continue;
+ }
+
+ AppendLine(inMathBlock ? mathBuilder : markdownBuilder, line);
+ }
+
+ if (inMathBlock)
+ return [new(MarkdownRenderSegmentType.MARKDOWN, normalized)];
+
+ FlushMarkdownSegment();
+ return segments.Count > 0 ? segments : [new(MarkdownRenderSegmentType.MARKDOWN, normalized)];
+
+ void FlushMarkdownSegment()
+ {
+ if (markdownBuilder.Length == 0)
+ return;
+
+ segments.Add(new(MarkdownRenderSegmentType.MARKDOWN, markdownBuilder.ToString()));
+ markdownBuilder.Clear();
+ }
+ }
+
private static string NormalizeMarkdownForRendering(string text)
{
var cleaned = text.RemoveThinkTags().Trim();
@@ -221,6 +288,39 @@ public partial class ContentBlockComponent : MSGComponentBase
return content.Contains("", StringComparison.Ordinal) || content.Contains("/>", StringComparison.Ordinal);
}
+
+ private static bool TryUpdateCodeFenceState(string trimmedLine, ref string? activeCodeFenceMarker)
+ {
+ string? fenceMarker = null;
+ if (trimmedLine.StartsWith("```", StringComparison.Ordinal))
+ fenceMarker = "```";
+ else if (trimmedLine.StartsWith("~~~", StringComparison.Ordinal))
+ fenceMarker = "~~~";
+
+ if (fenceMarker is null)
+ return false;
+
+ activeCodeFenceMarker = activeCodeFenceMarker is null
+ ? fenceMarker
+ : activeCodeFenceMarker == fenceMarker
+ ? null
+ : activeCodeFenceMarker;
+
+ return true;
+ }
+
+ private static void AppendLine(StringBuilder builder, string line)
+ {
+ builder.AppendLine(line);
+ }
+
+ private enum MarkdownRenderSegmentType
+ {
+ MARKDOWN,
+ MATH_BLOCK,
+ }
+
+ private readonly record struct MarkdownRenderSegment(MarkdownRenderSegmentType Type, string Content);
private async Task RemoveBlock()
{
diff --git a/app/MindWork AI Studio/Chat/MathJaxBlock.razor b/app/MindWork AI Studio/Chat/MathJaxBlock.razor
new file mode 100644
index 00000000..4e6c0ba2
--- /dev/null
+++ b/app/MindWork AI Studio/Chat/MathJaxBlock.razor
@@ -0,0 +1,5 @@
+@namespace AIStudio.Chat
+
+
+ @this.MathText
+
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Chat/MathJaxBlock.razor.cs b/app/MindWork AI Studio/Chat/MathJaxBlock.razor.cs
new file mode 100644
index 00000000..7f59e874
--- /dev/null
+++ b/app/MindWork AI Studio/Chat/MathJaxBlock.razor.cs
@@ -0,0 +1,30 @@
+using Microsoft.AspNetCore.Components;
+
+namespace AIStudio.Chat;
+
+public partial class MathJaxBlock
+{
+ private const string MATH_JAX_SCRIPT_ID = "mudblazor-markdown-mathjax";
+
+ [Parameter]
+ public string Value { get; init; } = string.Empty;
+
+ [Parameter]
+ public string Class { get; init; } = string.Empty;
+
+ [Inject]
+ private IJSRuntime JsRuntime { get; init; } = null!;
+
+ private string RootClass => string.IsNullOrWhiteSpace(this.Class)
+ ? "chat-mathjax-block"
+ : $"chat-mathjax-block {this.Class}";
+
+ private string MathText => $"$${Environment.NewLine}{this.Value}{Environment.NewLine}$$";
+
+ protected override async Task OnAfterRenderAsync(bool firstRender)
+ {
+ await this.JsRuntime.InvokeVoidAsync("appendMathJaxScript", MATH_JAX_SCRIPT_ID);
+ await this.JsRuntime.InvokeVoidAsync("refreshMathJaxScript");
+ await base.OnAfterRenderAsync(firstRender);
+ }
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/wwwroot/app.css b/app/MindWork AI Studio/wwwroot/app.css
index cd80c5a9..909d350d 100644
--- a/app/MindWork AI Studio/wwwroot/app.css
+++ b/app/MindWork AI Studio/wwwroot/app.css
@@ -150,4 +150,19 @@
.sources-card-header {
top: 0em !important;
left: 2.2em !important;
-}
\ No newline at end of file
+}
+
+.chat-mathjax-block {
+ text-align: left;
+}
+
+.chat-mathjax-block mjx-container[display="true"] {
+ text-align: left !important;
+ margin-left: 0 !important;
+ margin-right: 0 !important;
+}
+
+.chat-mathjax-block mjx-container[display="true"] mjx-math {
+ margin-left: 0 !important;
+ margin-right: 0 !important;
+}