feat: styles for essays.

This commit is contained in:
jackfiled 2025-01-24 16:29:32 +08:00
parent 24fb498d59
commit f071b02ae3
12 changed files with 129 additions and 91 deletions

View File

@ -18,9 +18,11 @@
文章
</div>
<div>
@(Contents.Count)
</div>
<a href="/blog/archives/">
<div>
@(Contents.Count)
</div>
</a>
</div>
<div class="flex flex-row justify-between px-6 py-2 text-xl">
@ -28,9 +30,11 @@
标签
</div>
<div>
@(Contents.Tags.Count)
</div>
<a href="/blog/tags/">
<div>
@(Contents.Tags.Count)
</div>
</a>
</div>
<div class="text-xl px-2 py-2">

View File

@ -9,8 +9,8 @@
<div>
文章地址:
<a href="/blog/essays/@(EssayAddress)" target="_blank" class="text-blue-600">
@($"https://rrricardo.top/blog/essays/{EssayAddress}")
<a href="/blog/essays/@(EssayFilename)" target="_blank" class="text-blue-600">
@($"https://rrricardo.top/blog/essays/{EssayFilename}")
</a>
</div>
@ -19,15 +19,15 @@
<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" class="text-blue-600">
CC BY-NC-SA 4.0
</a>
许可协议,转载请注明来
许可协议,诸位读者如有兴趣可任意转载,不必征询许可,但请注明“转载
<a href="https://rrricardo.top/blog/" target="_blank" class="text-blue-600">
Ricardo's Blog
</a>。
</a>
</div>
</div>
</div>
@code
{
[Parameter] public string? EssayAddress { get; set; }
[Parameter] public string? EssayFilename { get; set; }
}

View File

@ -22,9 +22,8 @@ public static class WebApplicationBuilderExtensions
builder.Services.AddSingleton<RendererService>();
builder.Services.AddSingleton<IEssayContentService, EssayContentService>();
builder.Services.AddTransient<ImagePostRenderProcessor>();
builder.Services.AddTransient<CodeBlockPostRenderProcessor>();
builder.Services.AddTransient<TablePostRenderProcessor>();
builder.Services.AddTransient<HeadlinePostRenderProcessor>();
builder.Services.AddTransient<EssayStylesPostRenderProcessor>();
builder.Services.AddTransient<BlogOptions>(provider =>
provider.GetRequiredService<IOptions<BlogOptions>>().Value);

View File

@ -9,9 +9,8 @@ public static class WebApplicationExtensions
public static void UseYaeBlog(this WebApplication application)
{
application.UsePostRenderProcessor<ImagePostRenderProcessor>();
application.UsePostRenderProcessor<CodeBlockPostRenderProcessor>();
application.UsePostRenderProcessor<TablePostRenderProcessor>();
application.UsePostRenderProcessor<HeadlinePostRenderProcessor>();
application.UsePostRenderProcessor<EssayStylesPostRenderProcessor>();
}
private static void UsePreRenderProcessor<T>(this WebApplication application) where T : IPreRenderProcessor

View File

@ -41,10 +41,14 @@
</div>
<div class="grid grid-cols-3">
<div class="col-span-3 md:col-span-2">
@((MarkupString)_essay!.HtmlContent)
<div class="col-span-3 md:col-span-2 flex flex-col gap-3">
<div>
@((MarkupString)_essay!.HtmlContent)
</div>
<LicenseDisclaimer EssayAddress="@BlogKey"/>
<div>
<LicenseDisclaimer EssayFilename="@BlogKey"/>
</div>
</div>
<div class="col-span-3 md:col-span-1">

View File

@ -38,7 +38,7 @@
<li class="p-2">
<div class="flex flex-row">
<a href="/blog/tags/?tagName=@(pair.Key.UrlEncodedTagName)">
<div class="text-blue-600 text-lg">
<div class="text-sky-600 text-lg">
# @(pair.Key.TagName)
</div>
</a>

View File

@ -1,29 +0,0 @@
using AngleSharp;
using AngleSharp.Dom;
using YaeBlog.Abstraction;
using YaeBlog.Models;
namespace YaeBlog.Processors;
public class CodeBlockPostRenderProcessor : IPostRenderProcessor
{
public async Task<BlogEssay> ProcessAsync(BlogEssay essay)
{
BrowsingContext context = new(Configuration.Default);
IDocument document = await context.OpenAsync(
req => req.Content(essay.HtmlContent));
IEnumerable<IElement> preElements = from e in document.All
where e.LocalName == "pre"
select e;
foreach (IElement element in preElements)
{
element.ClassList.Add("p-3 text-bg-secondary rounded-1");
}
return essay.WithNewHtmlContent(document.DocumentElement.OuterHtml);
}
public string Name => nameof(CodeBlockPostRenderProcessor);
}

View File

@ -0,0 +1,102 @@
using AngleSharp;
using AngleSharp.Dom;
using YaeBlog.Abstraction;
using YaeBlog.Models;
namespace YaeBlog.Processors;
/// <summary>
/// 向渲染的HTML中插入Tailwind CSS的渲染后处理器
/// </summary>
public sealed class EssayStylesPostRenderProcessor : IPostRenderProcessor
{
public string Name => nameof(EssayStylesPostRenderProcessor);
public async Task<BlogEssay> ProcessAsync(BlogEssay essay)
{
BrowsingContext context = new(Configuration.Default);
IDocument document = await context.OpenAsync(
req => req.Content(essay.HtmlContent));
ApplyGlobalCssStyles(document);
BeatifyTable(document);
return essay.WithNewHtmlContent(document.DocumentElement.OuterHtml);
}
private readonly Dictionary<string, string> _globalCssStyles = new()
{
{ "pre", "p-4 bg-slate-300 rounded-sm overflow-x-auto" },
{ "h2", "text-3xl font-bold py-4" },
{ "h3", "text-2xl font-bold py-3" },
{ "h4", "text-xl font-bold py-2" },
{ "h5", "text-lg font-bold py-1" },
{ "p", "p-2" },
{ "img", "w-11/12 block mx-auto my-2 rounded-md shadow-md" },
{ "ul", "list-disc pl-2" }
};
private void ApplyGlobalCssStyles(IDocument document)
{
foreach ((string tag, string style) in _globalCssStyles)
{
foreach (IElement element in document.GetElementsByTagName(tag))
{
element.ClassList.Add(style);
}
}
}
private static void BeatifyTable(IDocument document)
{
foreach (IElement element in from e in document.All
where e.LocalName == "table"
select e)
{
element.ClassList.Add("mx-auto border-collapse table-auto overflow-x-auto");
// thead元素
foreach (IElement headElement in from e in element.Children
where e.LocalName == "thead"
select e)
{
headElement.ClassList.Add("bg-slate-200");
// tr in thead
foreach (IElement trElement in from e in headElement.Children
where e.LocalName == "tr"
select e)
{
trElement.ClassList.Add("border border-slate-300");
// th in tr
foreach (IElement thElement in from e in trElement.Children
where e.LocalName == "th"
select e)
{
thElement.ClassList.Add("px-4 py-1");
}
}
}
// tbody元素
foreach (IElement bodyElement in from e in element.Children
where e.LocalName == "tbody"
select e)
{
// tr in tbody
foreach (IElement trElement in from e in bodyElement.Children
where e.LocalName == "tr"
select e)
{
foreach (IElement tdElement in from e in trElement.Children
where e.LocalName == "td"
select e)
{
tdElement.ClassList.Add("px-4 py-1 border border-slate-300");
}
}
}
}
}
}

View File

@ -11,13 +11,11 @@ public class ImagePostRenderProcessor(ILogger<ImagePostRenderProcessor> logger,
IOptions<BlogOptions> options)
: IPostRenderProcessor
{
private static readonly AngleSharp.IConfiguration s_configuration = Configuration.Default;
private readonly BlogOptions _options = options.Value;
public async Task<BlogEssay> ProcessAsync(BlogEssay essay)
{
BrowsingContext context = new(s_configuration);
BrowsingContext context = new(Configuration.Default);
IDocument html = await context.OpenAsync(
req => req.Content(essay.HtmlContent));
@ -33,7 +31,6 @@ public class ImagePostRenderProcessor(ILogger<ImagePostRenderProcessor> logger,
logger.LogDebug("Found image link: '{}'", attr.Value);
attr.Value = GenerateImageLink(attr.Value, essay.FileName);
}
element.ClassList.Add("essay-image");
}
return essay.WithNewHtmlContent(html.DocumentElement.OuterHtml);
}

View File

@ -1,34 +0,0 @@
using AngleSharp;
using AngleSharp.Dom;
using AngleSharp.Html.Dom;
using YaeBlog.Abstraction;
using YaeBlog.Models;
namespace YaeBlog.Processors;
public class TablePostRenderProcessor: IPostRenderProcessor
{
public async Task<BlogEssay> ProcessAsync(BlogEssay essay)
{
BrowsingContext browsingContext = new(Configuration.Default);
IDocument document = await browsingContext.OpenAsync(
req => req.Content(essay.HtmlContent));
IEnumerable<IHtmlTableElement> tableElements = from item in document.All
where item.LocalName == "table"
select item as IHtmlTableElement;
foreach (IHtmlTableElement element in tableElements)
{
IHtmlDivElement divElement = document.CreateElement<IHtmlDivElement>();
divElement.InnerHtml = element.OuterHtml;
divElement.ClassList.Add("py-2", "table-wrapper");
element.Replace(divElement);
}
return essay.WithNewHtmlContent(document.DocumentElement.OuterHtml);
}
public string Name => nameof(TablePostRenderProcessor);
}

View File

@ -7,10 +7,6 @@
<PackageReference Include="YamlDotNet" Version="16.2.1"/>
</ItemGroup>
<ItemGroup>
<Watch Remove="**\*.razor~"/>
</ItemGroup>
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>

View File

@ -1,6 +1,6 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["**/*.razor", "**/*.cshtml", "**/*.html"],
content: ["**/*.razor", "**/*.cshtml", "**/*.html", "Processors/EssayStylesPostRenderProcessor.cs"],
theme: {
extend: {},
},