diff --git a/app/MindWork AI Studio.sln b/app/MindWork AI Studio.sln
index 0bb1ab52..ab62feb1 100644
--- a/app/MindWork AI Studio.sln
+++ b/app/MindWork AI Studio.sln
@@ -8,6 +8,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Build Script", "Build\Build
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedTools", "SharedTools\SharedTools.csproj", "{969C74DF-7678-4CD5-B269-D03E1ECA3D2A}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceGeneratedMappings", "SourceGeneratedMappings\SourceGeneratedMappings.csproj", "{4D7141D5-9C22-4D85-B748-290D15FF484C}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -30,6 +32,10 @@ Global
{969C74DF-7678-4CD5-B269-D03E1ECA3D2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{969C74DF-7678-4CD5-B269-D03E1ECA3D2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{969C74DF-7678-4CD5-B269-D03E1ECA3D2A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4D7141D5-9C22-4D85-B748-290D15FF484C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4D7141D5-9C22-4D85-B748-290D15FF484C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4D7141D5-9C22-4D85-B748-290D15FF484C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4D7141D5-9C22-4D85-B748-290D15FF484C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
EndGlobalSection
diff --git a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor
index 4553ebc2..9e10140c 100644
--- a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor
+++ b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor
@@ -23,7 +23,7 @@
MaxLength="@textArea.MaxLength"
Immediate="@textArea.IsImmediate"
Adornment="@textArea.GetAdornmentPos()"
- AdornmentIcon="@textArea.AdornmentIcon"
+ AdornmentIcon="@textArea.GetIconSvg()"
AdornmentText="@textArea.AdornmentText"
AdornmentColor="@textArea.GetAdornmentColor()"
Variant="Variant.Outlined"
diff --git a/app/MindWork AI Studio/MindWork AI Studio.csproj b/app/MindWork AI Studio/MindWork AI Studio.csproj
index 0659eb96..8c9725e2 100644
--- a/app/MindWork AI Studio/MindWork AI Studio.csproj
+++ b/app/MindWork AI Studio/MindWork AI Studio.csproj
@@ -62,6 +62,19 @@
+
+ ..\SourceGeneratedMappings\SourceGeneratedMappings.csproj
+ ..\SourceGeneratedMappings\bin\$(Configuration)\net9.0\SourceGeneratedMappings.dll
+
+
+
+
+
+
+
+
+
+
diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantTextArea.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantTextArea.cs
index 09fb3b5b..dd4336c6 100644
--- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantTextArea.cs
+++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantTextArea.cs
@@ -1,3 +1,5 @@
+using AIStudio.Tools.PluginSystem.Assistants.Icons;
+
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
internal sealed class AssistantTextArea : AssistantComponentBase
@@ -111,4 +113,6 @@ internal sealed class AssistantTextArea : AssistantComponentBase
public Adornment GetAdornmentPos() => Enum.TryParse(this.Adornment, out var position) ? position : MudBlazor.Adornment.Start;
public Color GetAdornmentColor() => Enum.TryParse(this.AdornmentColor, out var color) ? color : Color.Default;
+
+ public string GetIconSvg() => MudBlazorIconRegistry.TryGetSvg(this.AdornmentIcon, out var svg) ? svg : string.Empty;
}
diff --git a/app/SourceGeneratedMappings/MappingRegistryGenerator.cs b/app/SourceGeneratedMappings/MappingRegistryGenerator.cs
new file mode 100644
index 00000000..668f0d72
--- /dev/null
+++ b/app/SourceGeneratedMappings/MappingRegistryGenerator.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+
+namespace SourceGeneratedMappings;
+
+[Generator]
+public sealed class MappingRegistryGenerator : IIncrementalGenerator
+{
+ private const string GENERATED_NAMESPACE = "AIStudio.Tools.PluginSystem.Assistants.Icons";
+ private const string ROOT_TYPE_NAME = "MudBlazor.Icons";
+ private static readonly string[] ALLOWED_GROUP_PATHS = ["Material.Filled", "Material.Outlined"];
+
+ private static readonly DiagnosticDescriptor ROOT_TYPE_MISSING = new(
+ id: "MBI001",
+ title: "MudBlazor icon root type was not found",
+ messageFormat: "The generator could not find '{0}' in the current compilation references. No icon registry was generated.",
+ category: "SourceGeneration",
+ DiagnosticSeverity.Info,
+ isEnabledByDefault: true);
+
+ private static readonly DiagnosticDescriptor NO_ICONS_FOUND = new(
+ id: "MBI002",
+ title: "No MudBlazor icons were discovered",
+ messageFormat: "The generator found '{0}', but no nested icon constants were discovered below it.",
+ category: "SourceGeneration",
+ DiagnosticSeverity.Warning,
+ isEnabledByDefault: true);
+
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ context.RegisterSourceOutput(context.CompilationProvider, static (spc, compilation) =>
+ {
+ Generate(spc, compilation);
+ });
+ }
+
+ private static void Generate(SourceProductionContext context, Compilation compilation)
+ {
+ var rootType = compilation.GetTypeByMetadataName(ROOT_TYPE_NAME);
+ if (rootType is null)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(ROOT_TYPE_MISSING, Location.None, ROOT_TYPE_NAME));
+ return;
+ }
+
+ var icons = new List();
+ CollectIcons(rootType, new List(), icons);
+
+ if (icons.Count == 0)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(NO_ICONS_FOUND, Location.None, ROOT_TYPE_NAME));
+ return;
+ }
+
+ var source = RenderSource(icons);
+ context.AddSource("MudBlazorIconRegistry.g.cs", SourceText.From(source, Encoding.UTF8));
+ }
+
+ private static void CollectIcons(INamedTypeSymbol currentType, List path, List icons)
+ {
+ foreach (var nestedType in currentType.GetTypeMembers().OrderBy(static t => t.Name, StringComparer.Ordinal))
+ {
+ path.Add(nestedType.Name);
+ CollectIcons(nestedType, path, icons);
+ path.RemoveAt(path.Count - 1);
+ }
+
+ foreach (var field in currentType.GetMembers().OfType().OrderBy(static f => f.Name, StringComparer.Ordinal))
+ {
+ if (!field.IsConst || field.Type.SpecialType != SpecialType.System_String || field.ConstantValue is not string svg)
+ continue;
+
+ if (path.Count == 0)
+ continue;
+
+ var groupPath = string.Join(".", path);
+ if (!ALLOWED_GROUP_PATHS.Contains(groupPath, StringComparer.Ordinal))
+ continue;
+
+ icons.Add(new IconDefinition(
+ QualifiedName: $"Icons.{groupPath}.{field.Name}",
+ Svg: svg));
+ }
+ }
+
+ private static string RenderSource(IReadOnlyList icons)
+ {
+ var builder = new StringBuilder();
+
+ builder.AppendLine("// ");
+ builder.AppendLine("#nullable enable");
+ builder.AppendLine("using System;");
+ builder.AppendLine("using System.Collections.Generic;");
+ builder.AppendLine();
+ builder.Append("namespace ").Append(GENERATED_NAMESPACE).AppendLine(";");
+ builder.AppendLine();
+ builder.AppendLine("public static class MudBlazorIconRegistry");
+ builder.AppendLine("{");
+ builder.AppendLine(" public static readonly IReadOnlyDictionary SvgByIdentifier = new Dictionary(StringComparer.Ordinal)");
+ builder.AppendLine(" {");
+
+ foreach (var icon in icons)
+ {
+ builder.Append(" [")
+ .Append(ToLiteral(icon.QualifiedName))
+ .Append("] = ")
+ .Append(ToLiteral(icon.Svg))
+ .AppendLine(",");
+ }
+
+ builder.AppendLine(" };");
+ builder.AppendLine();
+ builder.AppendLine(" public static bool TryGetSvg(string identifier, out string svg)");
+ builder.AppendLine(" {");
+ builder.AppendLine(" return SvgByIdentifier.TryGetValue(identifier, out svg!);");
+ builder.AppendLine(" }");
+ builder.AppendLine("}");
+
+ return builder.ToString();
+ }
+
+ private static string ToLiteral(string value)
+ {
+ return Microsoft.CodeAnalysis.CSharp.SymbolDisplay.FormatLiteral(value, quote: true);
+ }
+
+ private sealed record IconDefinition(string QualifiedName, string Svg);
+}
diff --git a/app/SourceGeneratedMappings/SourceGeneratedMappings.csproj b/app/SourceGeneratedMappings/SourceGeneratedMappings.csproj
new file mode 100644
index 00000000..aa671143
--- /dev/null
+++ b/app/SourceGeneratedMappings/SourceGeneratedMappings.csproj
@@ -0,0 +1,29 @@
+
+
+
+ net9.0
+ false
+ enable
+ latest
+
+ true
+ true
+
+ SourceGeneratedMappings
+ SourceGeneratedMappings
+ 1.0.0
+ SourceGeneratedMappings
+
+
+
+
+ $(MSBuildSDKsPath)\..\Roslyn\bincore\Microsoft.CodeAnalysis.dll
+ false
+
+
+ $(MSBuildSDKsPath)\..\Roslyn\bincore\Microsoft.CodeAnalysis.CSharp.dll
+ false
+
+
+
+