Iceshrimp.NET/Iceshrimp.Frontend/Core/Miscellaneous/RenderMfm.cs
2024-06-29 00:46:47 +02:00

190 lines
No EOL
6.3 KiB
C#

using AngleSharp;
using AngleSharp.Dom;
using Iceshrimp.Parsing;
using Iceshrimp.Shared.Schemas;
using Microsoft.AspNetCore.Components;
namespace Iceshrimp.Frontend.Core.Miscellaneous;
public static class MfmRenderer
{
public static async Task<MarkupString> RenderString(string text, List<EmojiResponse> emoji)
{
var res = Mfm.parse(text);
var context = BrowsingContext.New();
var document = await context.OpenNewAsync();
var renderedMfm = RenderMultipleNodes(res, document, emoji);
var html = renderedMfm.ToHtml();
return new MarkupString(html);
}
private static INode RenderMultipleNodes(
IEnumerable<MfmNodeTypes.MfmNode> nodes, IDocument document, List<EmojiResponse> emoji
)
{
var el = document.CreateElement("span");
el.SetAttribute("mfm", "mfm");
el.ClassName = "mfm";
foreach (var node in nodes)
{
try
{
el.AppendNodes(RenderNode(node, document, emoji));
}
catch (NotImplementedException e)
{
var fallback = document.CreateElement("span");
fallback.TextContent = $"[Node type <{e.Message}> not implemented]";
el.AppendNodes(fallback);
}
}
return el;
}
private static INode RenderNode(MfmNodeTypes.MfmNode node, IDocument document, List<EmojiResponse> emoji)
{
var rendered = node switch
{
MfmNodeTypes.MfmCenterNode mfmCenterNode => throw new NotImplementedException($"{mfmCenterNode.GetType()}"),
MfmNodeTypes.MfmCodeBlockNode mfmCodeBlockNode => MfmCodeBlockNode(mfmCodeBlockNode, document),
MfmNodeTypes.MfmMathBlockNode mfmMathBlockNode =>
throw new NotImplementedException($"{mfmMathBlockNode.GetType()}"),
MfmNodeTypes.MfmQuoteNode mfmQuoteNode => throw new NotImplementedException($"{mfmQuoteNode.GetType()}"),
MfmNodeTypes.MfmSearchNode mfmSearchNode => throw new NotImplementedException($"{mfmSearchNode.GetType()}"),
MfmNodeTypes.MfmBlockNode mfmBlockNode => throw new NotImplementedException($"{mfmBlockNode.GetType()}"),
MfmNodeTypes.MfmBoldNode mfmBoldNode => MfmBoldNode(mfmBoldNode, document),
MfmNodeTypes.MfmEmojiCodeNode mfmEmojiCodeNode => MfmEmojiCodeNode(mfmEmojiCodeNode, document, emoji),
MfmNodeTypes.MfmFnNode mfmFnNode => throw new NotImplementedException($"{mfmFnNode.GetType()}"),
MfmNodeTypes.MfmHashtagNode mfmHashtagNode =>
throw new NotImplementedException($"{mfmHashtagNode.GetType()}"),
MfmNodeTypes.MfmInlineCodeNode mfmInlineCodeNode => MfmInlineCodeNode(mfmInlineCodeNode, document),
MfmNodeTypes.MfmItalicNode mfmItalicNode => MfmItalicNode(mfmItalicNode, document),
MfmNodeTypes.MfmLinkNode mfmLinkNode => MfmLinkNode(mfmLinkNode, document),
MfmNodeTypes.MfmMathInlineNode mfmMathInlineNode =>
throw new NotImplementedException($"{mfmMathInlineNode.GetType()}"),
MfmNodeTypes.MfmMentionNode mfmMentionNode => MfmMentionNode(mfmMentionNode, document),
MfmNodeTypes.MfmPlainNode mfmPlainNode => throw new NotImplementedException($"{mfmPlainNode.GetType()}"),
MfmNodeTypes.MfmSmallNode mfmSmallNode => throw new NotImplementedException($"{mfmSmallNode.GetType()}"),
MfmNodeTypes.MfmStrikeNode mfmStrikeNode => throw new NotImplementedException($"{mfmStrikeNode.GetType()}"),
MfmNodeTypes.MfmTextNode mfmTextNode => MfmTextNode(mfmTextNode, document),
MfmNodeTypes.MfmUrlNode mfmUrlNode => MfmUrlNode(mfmUrlNode, document),
MfmNodeTypes.MfmInlineNode mfmInlineNode => throw new NotImplementedException($"{mfmInlineNode.GetType()}"),
_ => throw new ArgumentOutOfRangeException(nameof(node))
};
if (node.Children.Length > 0)
{
foreach (var childNode in node.Children)
{
try
{
rendered.AppendNodes(RenderNode(childNode, document, emoji));
}
catch (NotImplementedException e)
{
var fallback = document.CreateElement("span");
fallback.TextContent = $"[Node type <{e.Message}> not implemented]";
rendered.AppendNodes(fallback);
}
}
}
return rendered;
}
private static INode MfmCodeBlockNode(MfmNodeTypes.MfmCodeBlockNode node, IDocument document)
{
var el = document.CreateElement("pre");
var childEl = document.CreateElement("code");
childEl.TextContent = node.Code;
el.AppendChild(childEl);
return el;
}
private static INode MfmInlineCodeNode(MfmNodeTypes.MfmInlineCodeNode node, IDocument document)
{
var el = document.CreateElement("code");
el.TextContent = node.Code;
return el;
}
private static INode MfmLinkNode(MfmNodeTypes.MfmLinkNode node, IDocument document)
{
var el = document.CreateElement("a");
el.SetAttribute("href", node.Url);
el.ClassName = "link-node";
return el;
}
private static INode MfmItalicNode(MfmNodeTypes.MfmItalicNode _, IDocument document)
{
var el = document.CreateElement("span");
el.SetAttribute("style", "font-style: italic");
return el;
}
private static INode MfmEmojiCodeNode(
MfmNodeTypes.MfmEmojiCodeNode node, IDocument document, List<EmojiResponse> emojiList
)
{
var el = document.CreateElement("span");
el.ClassName = "emoji";
var emoji = emojiList.Find(p => p.Name == node.Name);
if (emoji is null)
{
el.TextContent = node.Name;
}
else
{
var image = document.CreateElement("img");
image.SetAttribute("src", emoji.PublicUrl);
image.SetAttribute("alt", node.Name);
el.AppendChild(image);
}
return el;
}
private static INode MfmUrlNode(MfmNodeTypes.MfmUrlNode node, IDocument document)
{
var el = document.CreateElement("a");
el.SetAttribute("href", node.Url);
el.ClassName = "url-node";
el.TextContent = node.Url;
return el;
}
private static INode MfmBoldNode(MfmNodeTypes.MfmBoldNode _, IDocument document)
{
var el = document.CreateElement("strong");
return el;
}
private static INode MfmTextNode(MfmNodeTypes.MfmTextNode node, IDocument document)
{
var el = document.CreateElement("span");
el.TextContent = node.Text;
return el;
}
private static INode MfmMentionNode(MfmNodeTypes.MfmMentionNode node, IDocument document)
{
var link = document.CreateElement("a");
link.SetAttribute("href", $"/@{node.Acct}");
link.ClassName = "mention";
var userPart = document.CreateElement("span");
userPart.ClassName = "user";
userPart.TextContent = $"@{node.Username}";
link.AppendChild(userPart);
if (node.Host != null)
{
var hostPart = document.CreateElement("span");
hostPart.ClassName = "host";
hostPart.TextContent = $"@{node.Host.Value}";
link.AppendChild(hostPart);
}
return link;
}
}