diff --git a/app/MindWork AI Studio/Dialogs/SystemPromptDialog.razor b/app/MindWork AI Studio/Dialogs/SystemPromptDialog.razor
new file mode 100644
index 00000000..6a9880b4
--- /dev/null
+++ b/app/MindWork AI Studio/Dialogs/SystemPromptDialog.razor
@@ -0,0 +1,42 @@
+@inherits MSGComponentBase
+
+
+
+
+ @T("This is the complete system prompt the AI receives with your next message. AI Studio assembles it from the parts listed here.")
+
+
+ @* In the order in which the parts appear in the prompt below: *@
+
+
+
+
@T("Date and time")
+
@this.DateTimeText
+
+
+
@T("Chat template")
+
@this.ChatTemplateText
+
+
+
@T("Data from your data sources")
+
@this.RetrievedDataText
+
+
+
@T("Profile")
+
@this.ProfileText
+
+
+
@T("Tool instructions")
+
@this.ToolNamesText
+
+
+
+
+
+
+
+
+ @T("Close")
+
+
+
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Dialogs/SystemPromptDialog.razor.cs b/app/MindWork AI Studio/Dialogs/SystemPromptDialog.razor.cs
new file mode 100644
index 00000000..7dc65864
--- /dev/null
+++ b/app/MindWork AI Studio/Dialogs/SystemPromptDialog.razor.cs
@@ -0,0 +1,97 @@
+using AIStudio.Components;
+
+using Microsoft.AspNetCore.Components;
+
+namespace AIStudio.Dialogs;
+
+///
+/// Shows the system prompt a chat sends, together with the parts it was assembled from.
+///
+///
+/// Nobody types this prompt as it is sent. A chat template replaces the default, a profile, the
+/// retrieved data of a data source, and the policy of the selected tools are added to it, and the
+/// date goes in front. The list above the text names these parts, so a person does not have to
+/// work out from the text alone where a paragraph came from.
+///
+public partial class SystemPromptDialog : MSGComponentBase
+{
+ [CascadingParameter]
+ private IMudDialogInstance MudDialog { get; set; } = null!;
+
+ ///
+ /// The whole system prompt, as the provider receives it.
+ ///
+ [Parameter]
+ public string SystemPrompt { get; set; } = string.Empty;
+
+ ///
+ /// The name of the chat template whose prompt replaces the default, or empty when there is none.
+ ///
+ [Parameter]
+ public string ChatTemplateName { get; set; } = string.Empty;
+
+ ///
+ /// The name of the profile which takes part, or empty when there is none.
+ ///
+ [Parameter]
+ public string ProfileName { get; set; } = string.Empty;
+
+ ///
+ /// Whether the chat template forbids a profile, which explains why none takes part even though one may be selected.
+ ///
+ [Parameter]
+ public bool IsProfileForbiddenByChatTemplate { get; set; }
+
+ ///
+ /// Whether the prompt carries data retrieved from a data source.
+ ///
+ [Parameter]
+ public bool HasRetrievedData { get; set; }
+
+ ///
+ /// The names of the tools whose usage policy is part of the prompt.
+ ///
+ [Parameter]
+ public IReadOnlyList ToolNames { get; set; } = [];
+
+ ///
+ /// Whether the current date and time go in front of the prompt.
+ ///
+ [Parameter]
+ public bool IncludesDateTime { get; set; }
+
+ private string ChatTemplateText => string.IsNullOrWhiteSpace(this.ChatTemplateName)
+ ? T("None")
+ : this.ChatTemplateName;
+
+ private string ProfileText
+ {
+ get
+ {
+ if (this.IsProfileForbiddenByChatTemplate)
+ return T("Switched off by the chat template");
+
+ return string.IsNullOrWhiteSpace(this.ProfileName)
+ ? T("None")
+ : this.ProfileName;
+ }
+ }
+
+ //
+ // Not promised to be refreshed with the next message: the data is replaced only when a new
+ // retrieval finds something. Until then, what was retrieved earlier travels along again.
+ //
+ private string RetrievedDataText => this.HasRetrievedData
+ ? T("Included, retrieved for an earlier message")
+ : T("None");
+
+ private string ToolNamesText => this.ToolNames.Count == 0
+ ? T("None")
+ : string.Join(", ", this.ToolNames);
+
+ private string DateTimeText => this.IncludesDateTime
+ ? T("Included")
+ : T("Not included");
+
+ private void Close() => this.MudDialog.Close();
+}
\ No newline at end of file