Revert the silent source export of whole answers

This commit is contained in:
Thorsten Sommer 2026-09-23 13:04:18 +02:00
parent e6db7aea47
commit a7cecad289
Signed by untrusted user who does not match committer: tsommer
GPG Key ID: 371BBA77A02C0108
3 changed files with 1 additions and 61 deletions

View File

@ -738,13 +738,7 @@ public partial class ContentBlockComponent : MSGComponentBase
// here which would fall out of sync with the one in FileExportFormatExtensions.
//
if (format.UsesPandoc())
{
if (this.Content.TryGetMarkdownText(out var originalMarkdown) &&
PlainFileExport.TryExtractStandaloneSource(originalMarkdown, format, out var source))
await PlainFileExport.ToSourceFile(this.RustService, this.EffectiveExportTitle, format, source);
else
await PandocExport.ToDocument(this.RustService, this.PandocAvailability, this.EffectiveExportTitle, format, this.Content);
}
await PandocExport.ToDocument(this.RustService, this.PandocAvailability, this.EffectiveExportTitle, format, this.Content);
else if (this.Content.TryGetExportMarkdown(out var markdown))
await PlainFileExport.ToFile(this.RustService, this.EffectiveExportTitle, format, markdown);
}

View File

@ -1,7 +1,6 @@
using System.Text;
using AIStudio.Tools.PluginSystem;
using AIStudio.Tools.Rust;
using AIStudio.Tools.Services;
using Markdig.Extensions.Tables;
@ -16,34 +15,6 @@ public static class PlainFileExport
private static string TB(string fallbackEn) => I18N.I.T(fallbackEn, typeof(PlainFileExport).Namespace, nameof(PlainFileExport));
/// <summary>
/// Reads a complete HTML source file out of a message which consists only of a matching fenced
/// code block.
/// </summary>
/// <param name="markdown">The Markdown text the model wrote.</param>
/// <param name="format">The selected export format.</param>
/// <param name="source">The contents inside the fence, or an empty string when the message is
/// not a standalone source file.</param>
/// <returns>True, when the message is a complete HTML source file.</returns>
internal static bool TryExtractStandaloneSource(string markdown, FileExportFormat format, out string source)
{
source = string.Empty;
if (format is not FileExportFormat.HTML || string.IsNullOrWhiteSpace(markdown))
return false;
var document = Markdig.Markdown.Parse(markdown, Markdown.SAFE_MARKDOWN_PIPELINE);
if (document.Count is not 1 || document[0] is not FencedCodeBlock block || block.ClosingFencedCharCount is 0)
return false;
var language = block.Info?.Trim();
if (!string.Equals("html", language, StringComparison.OrdinalIgnoreCase))
return false;
source = block.Lines.ToString();
return true;
}
/// <summary>
/// Reads every table a message holds, in the order they appear in it.
/// </summary>
@ -212,30 +183,6 @@ public static class PlainFileExport
if (format.UsesPandoc() || format.ToFileTypeFilter() is not { } fileTypeFilter)
throw new ArgumentOutOfRangeException(nameof(format), format, "AI Studio cannot write this format itself.");
return await WriteFile(rustService, dialogTitle, format, fileContent, fileTypeFilter, fileName);
}
/// <summary>
/// Writes HTML source authored by the model without converting it.
/// </summary>
/// <param name="rustService">The Rust service, used for the save dialog.</param>
/// <param name="dialogTitle">The title of the save dialog.</param>
/// <param name="format">The HTML format.</param>
/// <param name="source">The source text inside the model's code fence.</param>
/// <returns>True, when the file was written.</returns>
internal static async Task<bool> ToSourceFile(RustService rustService, string dialogTitle, FileExportFormat format, string source)
{
if (format is not FileExportFormat.HTML || format.ToFileTypeFilter() is not { } fileTypeFilter)
throw new ArgumentOutOfRangeException(nameof(format), format, "AI Studio cannot write this format as an authored source file.");
return await WriteFile(rustService, dialogTitle, format, source, fileTypeFilter);
}
/// <summary>
/// Lets the user choose a path and writes text with the encoding of its format.
/// </summary>
private static async Task<bool> WriteFile(RustService rustService, string dialogTitle, FileExportFormat format, string fileContent, FileTypeFilter fileTypeFilter, string? fileName = null)
{
var response = await rustService.SaveFile(dialogTitle, [fileTypeFilter], format.ToSuggestedFileName(fileName));
if (response.UserCancelled)
{

View File

@ -70,7 +70,6 @@
- Fixed an assistant refusing to work after you switched on the cleanup of web content without having chosen a model first. Its button did nothing at all, and only closing the assistant and starting over helped. The cleanup now follows the model of the assistant you are working in, the moment you pick one.
- Fixed the Visual Briefing assistant (in preview) not scrolling, which put everything below the window edge out of reach and made the assistant unusable. The briefing preview is now shown at its intended size inside its frame, and switching between the desktop, tablet, and mobile view changes its width as it should.
- Fixed exported answers losing their sources. When an answer is based on web pages a tool read or on documents of your own, the exported file now lists those sources in every format AI Studio writes.
- Fixed exporting an answer made only of one complete HTML code block. AI Studio now saves the source directly, without Markdown fences or a Pandoc conversion.
- Fixed the copy button leaving the sources behind. Copy an answer, and its sources come along.
- Fixed a tile that opens a chat directly always demanding a workspace. Leave the workspace empty in the Assistant Builder, and the tile opens a disappearing chat instead.
- Fixed the same restriction for plugin authors: a direct-chat launcher can now open a chat without naming a workspace. The example assistant plugin shows both ways.