diff --git a/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs b/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs
index 0b003c53..4dc68e92 100644
--- a/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs
+++ b/app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs
@@ -873,4 +873,4 @@ public partial class ContentBlockComponent : MSGComponentBase
await this.DisposeMathContainerIfNeededAsync();
}
-}
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Tools/FileExportFormatExtensions.cs b/app/MindWork AI Studio/Tools/FileExportFormatExtensions.cs
index 2fde6b79..5a22f93b 100644
--- a/app/MindWork AI Studio/Tools/FileExportFormatExtensions.cs
+++ b/app/MindWork AI Studio/Tools/FileExportFormatExtensions.cs
@@ -103,6 +103,36 @@ public static class FileExportFormatExtensions
_ => string.Empty,
};
+ ///
+ /// Reads which format a model means when it names a language behind the opening fence of a
+ /// code block.
+ ///
+ ///
+ /// A model which answers with a finished file puts it into a code block and names its language,
+ /// as in ```html. Models do not agree on the spelling, so we accept the usual names of a format
+ /// in any case. Only formats which are plain text appear here: a code block holds text, never
+ /// a Word document.
+ ///
+ /// The language behind the opening fence, as Markdig reads it into
+ /// FencedCodeBlock.Info.
+ /// The format the language names, or NONE when it names none of ours.
+ /// True, when the language names a format AI Studio writes.
+ public static bool TryFromCodeFenceLanguage(string? language, out FileExportFormat format)
+ {
+ format = language?.Trim().ToLowerInvariant() switch
+ {
+ "html" => FileExportFormat.HTML,
+ "latex" or "tex" => FileExportFormat.LATEX,
+ "markdown" or "md" => FileExportFormat.MARKDOWN,
+ "csv" => FileExportFormat.CSV,
+ "tsv" => FileExportFormat.TSV,
+
+ _ => FileExportFormat.NONE,
+ };
+
+ return format is not FileExportFormat.NONE;
+ }
+
///
/// Returns the file name the save dialog starts with.
///
diff --git a/app/MindWork AI Studio/Tools/PlainFileExport.cs b/app/MindWork AI Studio/Tools/PlainFileExport.cs
index e2e1e788..a3ccf1fa 100644
--- a/app/MindWork AI Studio/Tools/PlainFileExport.cs
+++ b/app/MindWork AI Studio/Tools/PlainFileExport.cs
@@ -94,15 +94,7 @@ public static class PlainFileExport
///
private static (string Fallback, FileExportFormat Format, string Text)? ToContent(FencedCodeBlock block)
{
- var format = block.Info?.Trim() switch
- {
- "csv" => FileExportFormat.CSV,
- "tsv" => FileExportFormat.TSV,
-
- _ => FileExportFormat.NONE,
- };
-
- if (format is FileExportFormat.NONE)
+ if (!FileExportFormatExtensions.TryFromCodeFenceLanguage(block.Info, out var format) || format is not (FileExportFormat.CSV or FileExportFormat.TSV))
return null;
var content = block.Lines.ToString();
@@ -206,4 +198,4 @@ public static class PlainFileExport
return false;
}
}
-}
+}
\ No newline at end of file
diff --git a/app/Tests/Tools/FileExportFormatTests.cs b/app/Tests/Tools/FileExportFormatTests.cs
index 46e8cb9f..a15f0671 100644
--- a/app/Tests/Tools/FileExportFormatTests.cs
+++ b/app/Tests/Tools/FileExportFormatTests.cs
@@ -36,4 +36,37 @@ public sealed class FileExportFormatTests
FileExportFormat.HTML,
}), "A format was added to or removed from the export menu: say in FollowsPageAnchors whether its reader follows a page in a local link, then name it here.");
}
+
+ [TestCase("html", FileExportFormat.HTML)]
+ [TestCase("latex", FileExportFormat.LATEX)]
+ [TestCase("tex", FileExportFormat.LATEX)]
+ [TestCase("markdown", FileExportFormat.MARKDOWN)]
+ [TestCase("md", FileExportFormat.MARKDOWN)]
+ [TestCase("csv", FileExportFormat.CSV)]
+ [TestCase("tsv", FileExportFormat.TSV)]
+ [TestCase("HTML", FileExportFormat.HTML, Description = "Models do not agree on the case.")]
+ [TestCase("LaTeX", FileExportFormat.LATEX)]
+ [TestCase("CSV", FileExportFormat.CSV)]
+ [TestCase(" md ", FileExportFormat.MARKDOWN, Description = "Space around the name is no part of it.")]
+ public void AFenceLanguageNamesItsFormat(string language, FileExportFormat expectedFormat)
+ {
+ Assert.Multiple(() =>
+ {
+ Assert.That(FileExportFormatExtensions.TryFromCodeFenceLanguage(language, out var format), Is.True);
+ Assert.That(format, Is.EqualTo(expectedFormat));
+ });
+ }
+
+ [TestCase("css", TestName = "A language AI Studio writes no file for")]
+ [TestCase("docx", TestName = "A format no code block can hold")]
+ [TestCase("", TestName = "A fence without a language")]
+ [TestCase(null, TestName = "A fence Markdig read no language for")]
+ public void AnyOtherFenceLanguageNamesNoFormat(string? language)
+ {
+ Assert.Multiple(() =>
+ {
+ Assert.That(FileExportFormatExtensions.TryFromCodeFenceLanguage(language, out var format), Is.False);
+ Assert.That(format, Is.EqualTo(FileExportFormat.NONE));
+ });
+ }
}
\ No newline at end of file