3 Commits

Author SHA1 Message Date
c446012199 Merge branch 'master' into feat-hpc 2025-08-31 12:41:03 +08:00
3053b83bbf feat: a lot of notebooks. 2025-08-31 12:39:18 +08:00
457316971c draft: six notebooks 2025-06-16 00:45:16 +08:00
125 changed files with 431 additions and 1260 deletions

View File

@@ -15,9 +15,6 @@ trim_trailing_whitespace = true
[project.json] [project.json]
indent_size = 2 indent_size = 2
[*.{yaml,yml}]
indent_size = 2
# C# and Visual Basic files # C# and Visual Basic files
[*.{cs,vb}] [*.{cs,vb}]
charset = utf-8-bom charset = utf-8-bom

View File

@@ -1,30 +1,33 @@
name: Build blog docker image name: Build blog docker image
on: on:
push: push:
branches: branches:
- master - master
jobs: jobs:
Build-Blog-Image: Build-Blog-Image:
runs-on: archlinux runs-on: archlinux
steps: steps:
- name: Check out code. - uses: https://mirrors.rrricardo.top/actions/checkout.git@v4
uses: https://mirrors.rrricardo.top/actions/checkout.git@v4 name: Check out code
with: with:
lfs: true lfs: true
- name: Build project. - name: Build project
run: | run: |
podman pull mcr.azure.cn/dotnet/aspnet:10.0 cd YaeBlog
cd YaeBlog dotnet publish
pwsh build.ps1 build - name: Build docker image
- name: Workaround to make sure podman-login working. run: |
run: | cd YaeBlog
mkdir /root/.docker podman build . -t registry.cn-beijing.aliyuncs.com/jackfiled/blog:latest --build-arg COMMIT_ID=$(git rev-parse --short=10 HEAD)
- name: Login tencent cloud docker registry. - name: Workaround to make sure podman login succeed
uses: https://mirrors.rrricardo.top/actions/podman-login.git@v1 run: |
with: mkdir /root/.docker
registry: ccr.ccs.tencentyun.com - name: Login aliyun docker registry
username: 100044380877 uses: https://mirrors.rrricardo.top/actions/podman-login.git@v1
password: ${{ secrets.TENCENT_REGISTRY_PASSWORD }} with:
auth_file_path: /etc/containers/auth.json registry: registry.cn-beijing.aliyuncs.com
- name: Push docker image. username: 初冬的朝阳
run: podman push ccr.ccs.tencentyun.com/jackfiled/blog:latest password: ${{ secrets.ALIYUN_PASSWORD }}
auth_file_path: /etc/containers/auth.json
- name: Push docker image
run: podman push registry.cn-beijing.aliyuncs.com/jackfiled/blog:latest

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "third-party/BlazorSvgComponents"]
path = third-party/BlazorSvgComponents
url = https://git.rrricardo.top/jackfiled/BlazorSvgComponents.git

View File

@@ -1,13 +0,0 @@
namespace YaeBlog.Tests;
public class DateTimeOffsetTests
{
[Fact]
public void DateTimeOffsetParseTest()
{
const string input = "2026-01-04T16:36:36.5629759+08:00";
DateTimeOffset time = DateTimeOffset.Parse(input);
Assert.Equal("2026年01月04日 16:36:36", time.ToString("yyyy年MM月dd日 HH:mm:ss"));
}
}

View File

@@ -1,25 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\YaeBlog\YaeBlog.csproj" />
</ItemGroup>
</Project>

View File

@@ -10,6 +10,5 @@
<File Path="LICENSE" /> <File Path="LICENSE" />
<File Path="README.md" /> <File Path="README.md" />
</Folder> </Folder>
<Project Path="YaeBlog.Tests/YaeBlog.Tests.csproj" />
<Project Path="YaeBlog/YaeBlog.csproj" /> <Project Path="YaeBlog/YaeBlog.csproj" />
</Solution> </Solution>

View File

@@ -6,11 +6,5 @@ public interface IEssayScanService
{ {
public Task<BlogContents> ScanContents(); public Task<BlogContents> ScanContents();
/// <summary>
/// 将对应的博客文章保存在磁盘上
/// </summary>
/// <param name="content"></param>
/// <param name="isDraft">指定对应博客文章是否为草稿。因为BlogContent是不可变对象因此提供该参数以方便publish的实现。</param>
/// <returns></returns>
public Task SaveBlogContent(BlogContent content, bool isDraft = true); public Task SaveBlogContent(BlogContent content, bool isDraft = true);
} }

View File

@@ -19,7 +19,6 @@ public sealed class YaeBlogCommand
AddWatchCommand(_rootCommand); AddWatchCommand(_rootCommand);
AddListCommand(_rootCommand); AddListCommand(_rootCommand);
AddNewCommand(_rootCommand); AddNewCommand(_rootCommand);
AddUpdateCommand(_rootCommand);
AddPublishCommand(_rootCommand); AddPublishCommand(_rootCommand);
AddScanCommand(_rootCommand); AddScanCommand(_rootCommand);
AddCompressCommand(_rootCommand); AddCompressCommand(_rootCommand);
@@ -47,7 +46,7 @@ public sealed class YaeBlogCommand
WebApplication application = builder.Build(); WebApplication application = builder.Build();
application.MapStaticAssets(); application.UseStaticFiles();
application.UseAntiforgery(); application.UseAntiforgery();
application.UseYaeBlog(); application.UseYaeBlog();
@@ -77,7 +76,7 @@ public sealed class YaeBlogCommand
WebApplication application = builder.Build(); WebApplication application = builder.Build();
application.MapStaticAssets(); application.UseStaticFiles();
application.UseAntiforgery(); application.UseAntiforgery();
application.UseYaeBlog(); application.UseYaeBlog();
@@ -110,12 +109,7 @@ public sealed class YaeBlogCommand
await essayScanService.SaveBlogContent(new BlogContent( await essayScanService.SaveBlogContent(new BlogContent(
new FileInfo(Path.Combine(blogOption.Value.Root, "drafts", file + ".md")), new FileInfo(Path.Combine(blogOption.Value.Root, "drafts", file + ".md")),
new MarkdownMetadata new MarkdownMetadata { Title = file, Date = DateTime.Now },
{
Title = file,
Date = DateTimeOffset.Now.ToString("o"),
UpdateTime = DateTimeOffset.Now.ToString("o")
},
string.Empty, true, [], [])); string.Empty, true, [], []));
Console.WriteLine($"Created new blog '{file}."); Console.WriteLine($"Created new blog '{file}.");
@@ -123,32 +117,6 @@ public sealed class YaeBlogCommand
new EssayScanServiceBinder()); new EssayScanServiceBinder());
} }
private static void AddUpdateCommand(RootCommand rootCommand)
{
Command newCommand = new("update", "Update the blog essay.");
rootCommand.AddCommand(newCommand);
Argument<string> filenameArgument = new(name: "blog name", description: "The blog filename to update.");
newCommand.AddArgument(filenameArgument);
newCommand.SetHandler(async (file, _, _, essayScanService) =>
{
Console.WriteLine("HINT: The update command only consider published blogs.");
BlogContents contents = await essayScanService.ScanContents();
BlogContent? content = contents.Posts.FirstOrDefault(c => c.BlogName == file);
if (content is null)
{
Console.WriteLine($"Target essay {file} is not exist.");
return;
}
content.Metadata.UpdateTime = DateTimeOffset.Now.ToString("o");
await essayScanService.SaveBlogContent(content, content.IsDraft);
}, filenameArgument,
new BlogOptionsBinder(), new LoggerBinder<EssayScanService>(), new EssayScanServiceBinder());
}
private static void AddListCommand(RootCommand rootCommand) private static void AddListCommand(RootCommand rootCommand)
{ {
Command command = new("list", "List all blogs"); Command command = new("list", "List all blogs");
@@ -243,8 +211,7 @@ public sealed class YaeBlogCommand
} }
// 设置发布的时间 // 设置发布的时间
content.Metadata.Date = DateTimeOffset.Now.ToString("o"); content.Metadata.Date = DateTime.Now;
content.Metadata.UpdateTime = DateTimeOffset.Now.ToString("o");
// 将选中的博客文件复制到posts // 将选中的博客文件复制到posts
await essayScanService.SaveBlogContent(content, isDraft: false); await essayScanService.SaveBlogContent(content, isDraft: false);

View File

@@ -3,44 +3,18 @@
<head> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<meta lang="zh-CN"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<base href="/"/> <base href="/"/>
<link rel="stylesheet" href="@Assets["YaeBlog.styles.css"]"/> <link rel="stylesheet" href="YaeBlog.styles.css"/>
<link rel="icon" href="@Assets["images/favicon.ico"]"/> <link rel="icon" href="images/favicon.ico"/>
<link rel="stylesheet" href="@Assets["tailwind.g.css"]"/> <link rel="stylesheet" href="globals.css"/>
<style> <link rel="stylesheet" href="tailwind.g.css"/>
@@font-face {
font-family: "Font Awesome 7 Free";
font-style: normal;
font-weight: 400;
font-display: block;
src: url(@Assets["fonts/fa-regular-400.woff2"]) format("woff2");
}
@@font-face {
font-family: "Font Awesome 7 Free";
font-style: normal;
font-weight: 900;
font-display: block;
src: url(@Assets["fonts/fa-solid-900.woff2"]) format("woff2")
}
@@font-face {
font-family: "Font Awesome 7 Brands";
font-style: normal;
font-weight: 400;
font-display: block;
src: url(@Assets["fonts/fa-brands-400.woff2"]) format("woff2")
}
</style>
<HeadOutlet/> <HeadOutlet/>
<ImportMap/>
</head> </head>
<body> <body>
<Routes/> <Routes/>
<script src="@Assets["_framework/blazor.web.js"]"></script> <script src="_framework/blazor.web.js"></script>
</body> </body>
</html> </html>

View File

@@ -3,7 +3,7 @@
<div class="flex flex-col p-3"> <div class="flex flex-col p-3">
<div class="text-3xl font-bold py-2"> <div class="text-3xl font-bold py-2">
<a href="@(Essay.EssayLink)">@(Essay.Title)</a> <a href="/blog/essays/@(Essay.FileName)" target="_blank">@(Essay.Title)</a>
</div> </div>
<div class="p-2 flex flex-row justify-content-start gap-2"> <div class="p-2 flex flex-row justify-content-start gap-2">
@@ -14,7 +14,9 @@
@foreach (string key in Essay.Tags) @foreach (string key in Essay.Tags)
{ {
<div class="text-sky-600"> <div class="text-sky-600">
<Anchor Address="@($"/blog/tags/?tagName={UrlEncoder.Default.Encode(key)}")" Text="@($"# {key}")"/> <a href="/blog/tags/?tagName=@(UrlEncoder.Default.Encode(key))">
# @key
</a>
</div> </div>
} }
</div> </div>

View File

@@ -2,7 +2,7 @@
<div> <div>
<p class="text-md"> <p class="text-md">
2021 - @(DateTimeOffset.Now.Year) © 2021 - @(DateTimeOffset.Now.Year) ©
<Anchor Address="https://rrricardot.top" Text="初冬的朝阳"/> <Anchor Address="https://rrricardot.top" Text="Ricardo Ren"/>
,由 ,由
<Anchor Address="https://dotnet.microsoft.com" Text="@DotnetVersion"/> <Anchor Address="https://dotnet.microsoft.com" Text="@DotnetVersion"/>
驱动。 驱动。
@@ -22,9 +22,9 @@
@code @code
{ {
private static string DotnetVersion => $".NET {Environment.Version}"; private string DotnetVersion => $".NET {Environment.Version}";
private static string BuildCommitId => Environment.GetEnvironmentVariable("COMMIT_ID") ?? "local_build"; private string BuildCommitId => Environment.GetEnvironmentVariable("COMMIT_ID") ?? "local_build";
private static string BuildCommitUrl => $"https://git.rrricardo.top/jackfiled/YaeBlog/commit/{BuildCommitId}"; private string BuildCommitUrl => $"https://git.rrricardo.top/jackfiled/YaeBlog/commit/{BuildCommitId}";
} }

View File

@@ -1,28 +0,0 @@
@inherits LayoutComponentBase
@attribute [StreamRendering]
<main class="container mx-auto flex flex-col min-h-screen">
<div class="grid grid-cols-3 mx-3">
<div class="md:col-span-2 col-span-3 h-20 flex items-center">
<a href="/blog/">
<span class="text-blue-600 text-2xl">Ricardo's Blog</span>
</a>
</div>
<div class="md:col-span-1 col-span-3 h-20 flex items-center">
<div class="flex flex-row w-full px-2 md:justify-center justify-end text-xl gap-3">
<Anchor Address="/blog/archives" Text="归档"/>
<Anchor Address="/blog/tags/" Text="标签"/>
<Anchor Address="/about/" Text="关于" NewPage="@(true)"/>
<Anchor Address="/friends" Text="友链" NewPage="@(true)"/>
</div>
</div>
</div>
<div class="px-4 py-2 flex-grow">
@Body
</div>
<Foonter/>
</main>

View File

@@ -1,92 +0,0 @@
@page "/about"
<PageTitle>
关于
</PageTitle>
<div class="flex flex-col">
<div>
<h1 class="text-4xl">关于</h1>
</div>
<div class="py-4">
<span class="italic">把字刻在石头上!(・’ω’・)</span>
</div>
<div class="flex flex-col p-2">
<div class="flex flex-col p-2">
<div class="pb-2">
<h3 class="text-2xl">关于我</h3>
</div>
<div class="mx-4">
<div class="my-4">
<p class="my-2">
正在明光村幼儿园附属研究生院攻读计算机科学与技术的硕士学位研究AI编译器和异构编译器。
</p>
<p class="my-2">
一般在互联网上使用<span class="italic">初冬的朝阳</span>或者<span
class="italic">jackfiled</span>的名字活动。
<span class="line-through">都是ICP备案过的人了网名似乎没有太大的用处</span>
</p>
</div>
<div class="my-4">
<p class="my-1">
主要是一个C#程序员目前也在尝试写一点Rust。
<span class="line-through">
总体上对于编程语言的态度是“大家都是我的翅膀.jpg”。
</span>
</p>
<p class="my-1">
写过一些前后端分离的项目对于RISC-V相关的开发项目也颇感兴趣。
</p>
<p class="my-1">
常常因为现实的压力而写一些C/C++现在就在和MLIR殊死搏斗。
</p>
<p class="my-1">
日常使用Arch Linux。
</p>
</div>
<div class="my-2">
<p class="my-1">
100%社恐。日常生活是宅在电脑前面自言自语。
</p>
<p class="my-1">
兴趣活动是读书和看番,目前在玩戴森球计划和三角洲。
</p>
</div>
</div>
</div>
<div class="flex flex-col p-2">
<div class="pb-2">
<h3 class="text-2xl">关于本站</h3>
</div>
<div class="mx-4">
<div class="my-4">
<p class="my-2">
本站肇始于2021年下半年在开始的两年中个人网站和博客是分别的两个网站个人网站是裸HTML写的博客是用
<Anchor Text="Hexo" Address="https://hexo.io" NewPage="@(true)"/>
的。
</p>
</div>
<div class="my-4">
<p class="my-2">
2024年我们决定使用.NET技术完全重构两个网站合二为一。虽然目前这个版本还是一个半成品但是我们一定会努力的~(确信。
</p>
</div>
<div class="my-4">
<p class="my-2">
2025年我们将使用的样式库从Bootstrap迁移到Tailwind CSS将现代的前端技术同Blazor结合起来。
</p>
</div>
</div>
</div>
</div>
</div>

View File

@@ -1,97 +0,0 @@
@page "/"
@using YaeBlog.Abstraction
@using YaeBlog.Models
@inject IEssayContentService EssayContentInstance
<PageTitle>
Ricardo's Index
</PageTitle>
<div class="mx-14 lg:mx-20">
<div class="grid grid-cols-3 py-4 lg:mx-20">
<div class="col-span-3 md:col-span-1 lg:m-10">
<img src="@Assets["images/avatar.png"]" alt="Ricardo's Avatar"
class="h-auto max-w-full rounded-md border border-gray-400">
</div>
<div class="col-span-3 md:col-span-2">
<div class="flex flex-col gap-y-3 items-center md:items-start md:px-6">
<div class="">
<div class="text-3xl font-bold">初冬的朝阳</div>
</div>
<div class="">
<p class="text-lg">a.k.a jackfiled</p>
</div>
<div class="">
<p class="text-lg italic">世界很大,时间很长。</p>
</div>
<div class="flex flex-row gap-2">
<a href="https://github.com/jackfiled" target="_blank">
<div>
<span class="fa-brands fa-github text-xl"></span>
</div>
</a>
<a href="https://git.rrricardo.top/jackfiled/" target="_blank">
<div>
<span class="gitea-icon text-xl"></span>
</div>
</a>
<a href="https://space.bilibili.com/378831522" target="_blank">
<div>
<span class="fa-brands fa-bilibili text-xl"></span>
</div>
</a>
<a href="https://xhslink.com/m/5GVDzyKf3De" target="_blank">
<div>
<span class="rednote-icon text-xl"></span>
</div>
</a>
</div>
</div>
</div>
</div>
<div class="py-5">
<p class="text-lg">恕我不能亲自为您沏茶,还是非常欢迎您来,能在广阔的互联网世界中发现这里实属不易。</p>
</div>
<div class="text-lg pt-2">
<p class="py-1">
正在攻读计算机科学与技术的硕士学位研究方向是AI编译和异构编译
</p>
<p class="py-1">
喜欢优雅的代码,香甜的蛋糕等等一切可爱的事物。
</p>
<p class="py-1">
<Anchor Address="/blog/" Text="个人博客"/>中收集了我的各种奇思妙想,如果感兴趣欢迎移步。
@if (_latestEssay is not null)
{
<span>
最新的一期博客关注 <Anchor Text="@(_latestEssay.Title)" Address="@(_latestEssay.EssayLink)"/>。
</span>
}
</p>
<p class="py-1">
日常的代码开发使用自建的<Anchor Text="Gitea" Address="https://git.rrricardo.top" NewPage="@(true)"/>进行,个人
开发的各种项目都可以在上面找到。
</p>
</div>
</div>
@code {
private BlogEssay? _latestEssay;
protected override void OnInitialized()
{
base.OnInitialized();
_latestEssay = EssayContentInstance.Essays.OrderByDescending(e => e.UpdateTime).FirstOrDefault();
}
}

View File

@@ -1,47 +0,0 @@
.fa-brands {
font-family: "Font Awesome 7 Brands", "Font Awesome 7 Free";
font-style: normal;
font-synthesis: none;
font-variant: normal;
line-height: 1;
text-rendering: auto;
font-weight: 400;
}
.fa-github::before {
content: "\f09b";
color: #24292e;
}
.fa-bilibili::before {
content: "\e3d9";
color: #00AEEC;
}
.gitea-icon {
display: inline-block;
vertical-align: -0.125em;
width: 1em;
height: 1em;
background-image: url("https://docs.gitea.com/img/gitea.svg");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
user-select: none;
}
.rednote-icon {
display: inline-block;
vertical-align: -0.125em;
width: 1em;
height: 1em;
background-image: url("images/xiaohongshu-seeklogo.svg");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
user-select: none;
}

View File

@@ -1,53 +0,0 @@
<Project>
<PropertyGroup>
<ClientAssetsRestoreCommand Condition="'$(ClientAssesRestoreCommand)' == ''">pnpm install</ClientAssetsRestoreCommand>
<ClientAssetsBuildCommand Condition="'$(ClientAssetsBuildCommand)' == ''">pnpm run build</ClientAssetsBuildCommand>
<ClientAssetsBuildOutputParameter Condition="'$(ClientAssetsBuildOutputParameter)' == ''">--output</ClientAssetsBuildOutputParameter>
</PropertyGroup>
<PropertyGroup>
<_RestoreClientAssetsBeforeTargets Condition="'$(TargetFramework)' == ''">DispatchToInnerBuilds</_RestoreClientAssetsBeforeTargets>
</PropertyGroup>
<Target Name="RestoreClientAssets" BeforeTargets="$(_RestoreClientAssetsBeforeTargets)">
<Message Importance="high" Text="Running $(ClientAssetsRestoreCommand)"/>
<Exec Command="$(ClientAssetsRestoreCommand)"/>
</Target>
<Target Name="BuildClientAssets" DependsOnTargets="RestoreClientAssets" BeforeTargets="AssignTargetPaths">
<PropertyGroup>
<_ClientAssetsOutputFullPath>$([System.IO.Path]::GetFullPath('$(IntermediateOutputPath)ClientAssets'))</_ClientAssetsOutputFullPath>
</PropertyGroup>
<MakeDir Directories="$(_ClientAssetsOutputFullPath)"/>
<Exec Command="$(ClientAssetsBuildCommand) $(ClientAssetsBuildOutputParameter) $(_ClientAssetsOutputFullPath)"/>
<ItemGroup>
<_ClientAssetsBuildOutput Include="$(IntermediateOutputPath)ClientAssets\**"/>
</ItemGroup>
</Target>
<Target Name="DefineClientAssets" AfterTargets="BuildClientAssets" DependsOnTargets="ResolveStaticWebAssetsConfiguration">
<ItemGroup>
<FileWrites Include="@(_ClientAssetsBuildOutput)"/>
</ItemGroup>
<DefineStaticWebAssets
CandidateAssets="@(_ClientAssetsBuildOutput)"
SourceId="$(PackageId)"
SourceType="Computed"
ContentRoot="$(_ClientAssetsOutputFullPath)"
BasePath="$(StaticWebAssetBasePath)"
>
<Output TaskParameter="Assets" ItemName="StaticWebAsset"/>
<Output TaskParameter="Assets" ItemName="_ClientAssetsStaticWebAsset"/>
</DefineStaticWebAssets>
<DefineStaticWebAssetEndpoints
CandidateAssets="@(_ClientAssetsStaticWebAsset)"
ContentTypeMappings="@(StaticWebAssetContentTypeMapping)"
>
<Output TaskParameter="Endpoints" ItemName="StaticWebAssetEndpoint" />
</DefineStaticWebAssetEndpoints>
</Target>
</Project>

View File

@@ -1,10 +1,10 @@
FROM mcr.azure.cn/dotnet/aspnet:10.0 FROM mcr.microsoft.com/dotnet/aspnet:9.0
ARG COMMIT_ID ARG COMMIT_ID
ENV COMMIT_ID=${COMMIT_ID} ENV COMMIT_ID=${COMMIT_ID}
WORKDIR /app WORKDIR /app
COPY bin/Release/net10.0/publish/ ./ COPY bin/Release/net9.0/publish/ ./
COPY source/ ./source/ COPY source/ ./source/
COPY appsettings.json . COPY appsettings.json .

View File

@@ -0,0 +1,44 @@
@inherits LayoutComponentBase
@attribute [StreamRendering]
<main class="container mx-auto flex flex-col min-h-screen">
<div class="grid grid-cols-3 mx-3">
<div class="md:col-span-2 col-span-3 h-20 flex items-center">
<a href="/blog/">
<span class="text-blue-600 text-2xl">Ricardo's Blog</span>
</a>
</div>
<div class="md:col-span-1 col-span-3 h-20 flex items-center">
<div class="flex flex-row w-full px-2 gap-3 md:justify-center justify-end">
<div>
<a href="/blog/archives/">
<span class="text-xl text-blue-600">归档</span>
</a>
</div>
<div>
<a href="/blog/tags/">
<span class="text-xl text-blue-600">标签</span>
</a>
</div>
<div>
<a href="/about/" target="_blank">
<span class="text-xl text-blue-600">关于</span>
</a>
</div>
<div>
<a href="/friends/" target="_blank">
<span class="text-xl text-blue-600">友链</span>
</a>
</div>
</div>
</div>
</div>
<div class="px-4 py-2 flex-grow">
@Body
</div>
<Foonter/>
</main>

View File

@@ -17,11 +17,13 @@
<Anchor <Anchor
Address="/about/" Address="/about/"
Text="关于"/> Text="关于"
NewPage="@(true)"/>
<Anchor <Anchor
Address="/friends" Address="/friends"
Text="友链"/> Text="友链"
NewPage="@(true)"/>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -8,9 +8,7 @@ public class BlogEssay : IComparable<BlogEssay>
public required bool IsDraft { get; init; } public required bool IsDraft { get; init; }
public required DateTimeOffset PublishTime { get; init; } public required DateTime PublishTime { get; init; }
public required DateTimeOffset UpdateTime { get; init; }
public required string Description { get; init; } public required string Description { get; init; }
@@ -22,8 +20,6 @@ public class BlogEssay : IComparable<BlogEssay>
public required string HtmlContent { get; init; } public required string HtmlContent { get; init; }
public string EssayLink => $"/blog/essays/{FileName}";
public BlogEssay WithNewHtmlContent(string newHtmlContent) public BlogEssay WithNewHtmlContent(string newHtmlContent)
{ {
var essay = new BlogEssay var essay = new BlogEssay
@@ -32,7 +28,6 @@ public class BlogEssay : IComparable<BlogEssay>
FileName = FileName, FileName = FileName,
IsDraft = IsDraft, IsDraft = IsDraft,
PublishTime = PublishTime, PublishTime = PublishTime,
UpdateTime = UpdateTime,
Description = Description, Description = Description,
WordCount = WordCount, WordCount = WordCount,
ReadTime = ReadTime, ReadTime = ReadTime,

View File

@@ -4,9 +4,7 @@ public class MarkdownMetadata
{ {
public string? Title { get; set; } public string? Title { get; set; }
public string? Date { get; set; } public DateTime? Date { get; set; }
public string? UpdateTime { get; set; }
public List<string>? Tags { get; set; } public List<string>? Tags { get; set; }
} }

79
YaeBlog/Pages/About.razor Normal file
View File

@@ -0,0 +1,79 @@
@page "/about"
<PageTitle>
关于
</PageTitle>
<div class="flex flex-col">
<div>
<h1 class="text-4xl">关于</h1>
</div>
<div class="py-4">
<span class="italic">把字刻在石头上!(・’ω’・)</span>
</div>
<div class="flex flex-col p-2">
<div class="flex flex-col p-2">
<div class="pb-2">
<h3 class="text-2xl">关于我</h3>
</div>
<div class="py-2">
计算机科学与技术在读大学生,明光村幼儿园附属大学所属。正处于读书和失业的叠加态。
一般在互联网上使用<span class="italic">初冬的朝阳</span>或者<span class="italic">jackfiled</span>的名字活动。
<span class="line-through">都是ICP备案过的人了网名似乎没有太大的用处</span>
</div>
<div class="py-2">
主要是一个C#程序员目前也在尝试写一点Rust。
总体上对于编程语言的态度是“<span>大家都是我的翅膀.jpg</span>”。
前后端分离的项目本当上手。
常常因为现实的压力而写一些C/C++。
<span class="line-through">对于Java和Go的评价很低。</span>
日常使用ArchLinux。
</div>
<div class="py-2">
100%社恐。日常生活是宅在电脑前面自言自语。
兴趣活动是读书和看番,目前在玩原神和三角洲。
</div>
<div class="py-4">
常常被人批评没有梦想,这里就随便瞎编一下。
成为嵌入式工程师,修好桌面上的<a href="https://www.bilibili.com/video/BV1VA411p7MD">HoloCubic</a>。
完成第一个不是课程设计的个人开源项目。
遇到能够搭伙过日子的人也算是一大梦想,虽然社恐人根本不知道从何开始的说,
<span class="line-through">什么时候天上才能掉美少女?</span>
</div>
<div class="py-2">
公开的联系渠道是<a href="mailto:shicangjuner@outlook.com">电子邮件</a>。
也可以试试在各大平台搜索上面提到的名字。
</div>
</div>
<div class="flex flex-col p-2">
<div class="pb-2">
<h3 class="text-2xl">关于本站</h3>
</div>
<div class="py-2">
本站肇始于2021年下半年在开始的两年中个人网站和博客是分别的两个网站个人网站是裸HTML写的博客是用
<a href="https://hexo.io">Hexo</a>渲染的。
</div>
<div class="py-2">
2024年我们决定使用.NET技术完全重构两个网站合二为一。虽然目前这个版本还是一个半成品但是我们一定会努力的~(确信。
</div>
<div class="py-2">
2025年我们将使用的样式库从Bootstrap迁移到Tailwind CSS将现代的前端技术同Blazor结合起来。
</div>
</div>
</div>
</div>
@code {
}

View File

@@ -19,7 +19,7 @@
</span> </span>
</div> </div>
@foreach (IGrouping<DateTimeOffset, BlogEssay> group in _essays) @foreach (IGrouping<DateTime, BlogEssay> group in _essays)
{ {
<div class="p-2"> <div class="p-2">
<div class="flex flex-col"> <div class="flex flex-col">
@@ -30,7 +30,7 @@
<div class="px-4 py-4 flex flex-col"> <div class="px-4 py-4 flex flex-col">
@foreach (BlogEssay essay in group) @foreach (BlogEssay essay in group)
{ {
<a href="@($"/blog/essays/{essay.FileName}")"> <a target="_blank" href="@($"/blog/essays/{essay.FileName}")">
<div class="flex flex-row p-2 mx-1 rounded-lg hover:bg-gray-300"> <div class="flex flex-row p-2 mx-1 rounded-lg hover:bg-gray-300">
<div class="w-20"> <div class="w-20">
@(essay.PublishTime.ToString("MM月dd日")) @(essay.PublishTime.ToString("MM月dd日"))
@@ -51,13 +51,13 @@
</div> </div>
@code { @code {
private readonly List<IGrouping<DateTimeOffset, BlogEssay>> _essays = []; private readonly List<IGrouping<DateTime, BlogEssay>> _essays = [];
protected override void OnInitialized() protected override void OnInitialized()
{ {
base.OnInitialized(); base.OnInitialized();
_essays.AddRange(from essay in Contents.Essays _essays.AddRange(from essay in Contents.Essays
group essay by new DateTimeOffset(essay.PublishTime.Year, 1, 1,0, 0, 0, TimeSpan.Zero)); group essay by new DateTime(essay.PublishTime.Year, 1, 1));
} }
} }

View File

@@ -39,11 +39,6 @@
{ {
_page = Page ?? 1; _page = Page ?? 1;
_pageCount = Contents.Count / EssaysPerPage + 1; _pageCount = Contents.Count / EssaysPerPage + 1;
(_pageCount, int reminder) = int.DivRem(Contents.Count, EssaysPerPage);
if (reminder > 0)
{
_pageCount += 1;
}
if (EssaysPerPage * _page > Contents.Count + EssaysPerPage) if (EssaysPerPage * _page > Contents.Count + EssaysPerPage)
{ {

View File

@@ -12,42 +12,47 @@
<div class="flex flex-col py-8"> <div class="flex flex-col py-8">
<div> <div>
<div class="flex flex-col items-center"> <h1 id="title" class="text-4xl">@(_essay!.Title)</h1>
<div> <div class="col-auto">
<h1 id="title" class="text-4xl">@(_essay!.Title)</h1> </div>
</div>
<div class="px-6 pt-4 pb-2">
<div class="flex flex-row gap-4">
<div class="font-light">
@(_essay!.PublishTime.ToString("yyyy-MM-dd"))
</div> </div>
<div class="flex flex-row gap-4 py-2"> @foreach (string tag in _essay!.Tags)
@foreach (string tag in _essay!.Tags)
{
<div class="text-sky-500">
<a href="/blog/tags/?tagName=@(UrlEncoder.Default.Encode(tag))">
# @(tag)
</a>
</div>
}
</div>
<div class="font-light pb-1">
发布于: @(_essay.PublishTime.ToString("yyyy年MM月dd日 HH:mm:ss"))
</div>
@if (_essay.UpdateTime != _essay.PublishTime)
{ {
<div class="font-light pb-1"> <div class="text-sky-500">
更新于: @(_essay.UpdateTime.ToString("yyyy年MM月dd日 HH:mm:ss")) <a href="/blog/tags/?tagName=@(UrlEncoder.Default.Encode(tag))">
# @(tag)
</a>
</div> </div>
} }
</div>
</div>
<div class="font-light pb-1"> <div class="px-6 pt-2 pb-4">
总字数:@(_essay!.WordCount)字,预计阅读时间 @(_essay!.ReadTime) <div class="font-light">
</div> 总字数:@(_essay!.WordCount)字,预计阅读时间 @(_essay!.ReadTime)。
</div> </div>
</div> </div>
<div class="grid grid-cols-3"> <div class="grid grid-cols-3">
<div class="col-span-3 md:col-span-2 flex flex-col gap-3">
<div>
@((MarkupString)_essay!.HtmlContent)
</div>
<div>
<LicenseDisclaimer EssayFilename="@BlogKey"/>
</div>
</div>
<div class="col-span-3 md:col-span-1"> <div class="col-span-3 md:col-span-1">
<div class="flex flex-col sticky top-20 px-8 pt-20"> <div class="flex flex-col sticky top-0 px-8">
<div> <div>
<h3 class="text-2xl">文章目录</h3> <h3 class="text-2xl">文章目录</h3>
</div> </div>
@@ -88,17 +93,8 @@
} }
</div> </div>
</div> </div>
<div class="col-span-3 md:col-span-2 flex flex-col gap-3">
<div>
@((MarkupString)_essay!.HtmlContent)
</div>
<div>
<LicenseDisclaimer EssayFilename="@BlogKey"/>
</div>
</div>
</div> </div>
</div> </div>
@code { @code {

59
YaeBlog/Pages/Index.razor Normal file
View File

@@ -0,0 +1,59 @@
@page "/"
<PageTitle>
Ricardo's Index
</PageTitle>
<div class="mx-20">
<div class="grid grid-cols-3 py-4">
<div class="col-span-3 md:col-span-1 p-5 p-lg-0">
<img src="images/avatar.png" alt="Ricardo's Avatar" class="h-auto max-w-full">
</div>
<div class="col-span-3 md:col-span-2">
<div class="flex flex-col px-3 gap-y-3">
<div class="">
<div class="text-3xl font-bold">初冬的朝阳 (Ricardo Ren)</div>
</div>
<div class="">
<p class="text-lg">a.k.a jackfiled</p>
</div>
<div class="">
<p class="text-lg italic">世界很大,时间很长。</p>
</div>
<div class="">
<p class="text-lg">
平平无奇的计算机科学与技术学徒,连微小的贡献都没做。
</p>
</div>
</div>
</div>
</div>
<div class="py-5">
<p class="text-lg">恕我不能亲自为您沏茶(?),还是非常欢迎您能够来到我的主页。</p>
</div>
<div>
<p class="text-lg py-1">
如果您想四处看看,了解一下屏幕对面的人,可以在我的 <Anchor Address="/blog/" Text="博客"/> 看看。
如果您对于明光村幼儿园某附属技校的计算机教学感兴趣,您可以移步到
<Anchor Address="https://jackfiled.github.io/wiki/" Text="我的学习笔记"/>
<span class="fs-5 text-decoration-line-through">虽然这笔记我自己也木有看过。</span>
如果您想批判一下我的代码,在
<Anchor Address="https://github.com/jackfiled/" Text="Github"/> 和
<Anchor Address="https://git.rrricardo.top/jackfiled/" Text="Gitea"/>
都可以找到。
</p>
<p class="text-lg py-1">
如果您真的很闲,也可以四处搜寻一下,也许存在着一些不为人知的彩蛋。
</p>
</div>
</div>
@code {
}

View File

@@ -16,7 +16,8 @@ public sealed class EssayStylesPostRenderProcessor : IPostRenderProcessor
public async Task<BlogEssay> ProcessAsync(BlogEssay essay) public async Task<BlogEssay> ProcessAsync(BlogEssay essay)
{ {
BrowsingContext context = new(Configuration.Default); BrowsingContext context = new(Configuration.Default);
IDocument document = await context.OpenAsync(req => req.Content(essay.HtmlContent)); IDocument document = await context.OpenAsync(
req => req.Content(essay.HtmlContent));
ApplyGlobalCssStyles(document); ApplyGlobalCssStyles(document);
BeatifyTable(document); BeatifyTable(document);
@@ -35,7 +36,6 @@ public sealed class EssayStylesPostRenderProcessor : IPostRenderProcessor
{ "h5", "text-lg font-bold py-1" }, { "h5", "text-lg font-bold py-1" },
{ "p", "p-2" }, { "p", "p-2" },
{ "img", "w-11/12 block mx-auto my-2 rounded-md shadow-md" }, { "img", "w-11/12 block mx-auto my-2 rounded-md shadow-md" },
{ "a", "text-blue-600" }
}; };
private void ApplyGlobalCssStyles(IDocument document) private void ApplyGlobalCssStyles(IDocument document)
@@ -102,33 +102,17 @@ public sealed class EssayStylesPostRenderProcessor : IPostRenderProcessor
} }
} }
/// <summary>
/// 美化各种列表元素
/// </summary>
/// <param name="document"></param>
private static void BeatifyList(IDocument document) private static void BeatifyList(IDocument document)
{ {
foreach (IElement listElement in from e in document.All foreach (IElement ulElement in from e in document.All
where e.LocalName is "ol" or "ul" where e.LocalName == "ul"
select e) select e)
{ {
// 给有序或者无序列表添加不同的样式 // 首先给<ul>元素添加样式
listElement.ClassList.Add("ml-10"); ulElement.ClassList.Add("list-disc ml-10");
switch (listElement.LocalName)
{
case "ul":
{
listElement.ClassList.Add("list-disc");
break;
}
case "ol":
{
listElement.ClassList.Add("list-decimal");
break;
}
}
foreach (IElement liElement in from e in listElement.Children
foreach (IElement liElement in from e in ulElement.Children
where e.LocalName == "li" where e.LocalName == "li"
select e) select e)
{ {

View File

@@ -16,11 +16,11 @@ public sealed class BlogHotReloadService(
await rendererService.RenderAsync(true); await rendererService.RenderAsync(true);
Task[] reloadTasks = [WatchFileAsync(stoppingToken)]; Task[] reloadTasks = [FileWatchTask(stoppingToken)];
await Task.WhenAll(reloadTasks); await Task.WhenAll(reloadTasks);
} }
private async Task WatchFileAsync(CancellationToken token) private async Task FileWatchTask(CancellationToken token)
{ {
while (!token.IsCancellationRequested) while (!token.IsCancellationRequested)
{ {
@@ -33,15 +33,6 @@ public sealed class BlogHotReloadService(
break; break;
} }
FileInfo changeFileInfo = new(changeFile);
if (changeFileInfo.Name.StartsWith('.'))
{
// Ignore dot-started file and directory.
logger.LogDebug("Ignore hidden file: {}.", changeFile);
continue;
}
logger.LogInformation("{} changed, re-rendering.", changeFile); logger.LogInformation("{} changed, re-rendering.", changeFile);
essayContentService.Clear(); essayContentService.Clear();
await rendererService.RenderAsync(true); await rendererService.RenderAsync(true);

View File

@@ -109,12 +109,6 @@ public partial class EssayScanService : IEssayScanService
{ {
foreach (BlogResult blog in fileContents) foreach (BlogResult blog in fileContents)
{ {
if (blog.BlogContent.Length < 4)
{
// Even not contains a legal header.
continue;
}
int endPos = blog.BlogContent.IndexOf("---", 4, StringComparison.Ordinal); int endPos = blog.BlogContent.IndexOf("---", 4, StringComparison.Ordinal);
if (!blog.BlogContent.StartsWith("---") || endPos is -1 or 0) if (!blog.BlogContent.StartsWith("---") || endPos is -1 or 0)
{ {
@@ -127,14 +121,14 @@ public partial class EssayScanService : IEssayScanService
try try
{ {
MarkdownMetadata metadata = _yamlDeserializer.Deserialize<MarkdownMetadata>(metadataString); MarkdownMetadata metadata = _yamlDeserializer.Deserialize<MarkdownMetadata>(metadataString);
_logger.LogDebug("Scan metadata title: '{title}' for {name}.", metadata.Title, blog.BlogFile.Name); _logger.LogDebug("Scan metadata title: '{}' for {}.", metadata.Title, blog.BlogFile.Name);
contents.Add(new BlogContent(blog.BlogFile, metadata, blog.BlogContent[(endPos + 3)..], isDraft, contents.Add(new BlogContent(blog.BlogFile, metadata, blog.BlogContent[(endPos + 3)..], isDraft,
blog.Images, blog.NotFoundImages)); blog.Images, blog.NotFoundImages));
} }
catch (YamlException e) catch (YamlException e)
{ {
_logger.LogWarning("Failed to parser metadata from {name} due to {exception}, skipping", blog.BlogFile.Name, e); _logger.LogWarning("Failed to parser metadata from {} due to {}, skipping", blog.BlogFile.Name, e);
} }
} }
}); });

View File

@@ -1,62 +0,0 @@
using YaeBlog.Extensions;
using YaeBlog.Models;
namespace YaeBlog.Services
{
public class MarkdownWordCounter
{
private bool _inCodeBlock;
private int _index;
private readonly string _content;
private uint WordCount { get; set; }
private MarkdownWordCounter(BlogContent content)
{
_content = content.Content;
}
private void CountWordInner()
{
while (_index < _content.Length)
{
if (IsCodeBlockTag())
{
_inCodeBlock = !_inCodeBlock;
}
if (!_inCodeBlock && char.IsLetterOrDigit(_content, _index))
{
WordCount += 1;
}
_index++;
}
}
private bool IsCodeBlockTag()
{
// 首先考虑识别代码块
bool outerCodeBlock =
Enumerable.Range(0, 3)
.Select(i => _index + i < _content.Length && _content.AsSpan().Slice(_index + i, 1) is "`")
.All(i => i);
if (outerCodeBlock)
{
return true;
}
// 然后识别行内代码
return _index < _content.Length && _content.AsSpan().Slice(_index, 1) is "`";
}
public static uint CountWord(BlogContent content)
{
MarkdownWordCounter counter = new(content);
counter.CountWordInner();
return counter.WordCount;
}
}
}

View File

@@ -9,7 +9,7 @@ using YaeBlog.Models;
namespace YaeBlog.Services; namespace YaeBlog.Services;
public sealed partial class RendererService( public partial class RendererService(
ILogger<RendererService> logger, ILogger<RendererService> logger,
IEssayScanService essayScanService, IEssayScanService essayScanService,
MarkdownPipeline markdownPipeline, MarkdownPipeline markdownPipeline,
@@ -38,15 +38,7 @@ public sealed partial class RendererService(
List<BlogEssay> essays = []; List<BlogEssay> essays = [];
foreach (BlogContent content in preProcessedContents) foreach (BlogContent content in preProcessedContents)
{ {
(uint wordCount, string readTime) = GetWordCount(content); uint wordCount = GetWordCount(content);
DateTimeOffset publishDate = content.Metadata.Date is null
? DateTimeOffset.Now
: DateTimeOffset.Parse(content.Metadata.Date);
// 如果不存在最后的更新时间,就把更新时间设置为发布时间
DateTimeOffset updateTime = content.Metadata.UpdateTime is null
? publishDate
: DateTimeOffset.Parse(content.Metadata.UpdateTime);
BlogEssay essay = new() BlogEssay essay = new()
{ {
Title = content.Metadata.Title ?? content.BlogName, Title = content.Metadata.Title ?? content.BlogName,
@@ -54,9 +46,8 @@ public sealed partial class RendererService(
IsDraft = content.IsDraft, IsDraft = content.IsDraft,
Description = GetDescription(content), Description = GetDescription(content),
WordCount = wordCount, WordCount = wordCount,
ReadTime = readTime, ReadTime = CalculateReadTime(wordCount),
PublishTime = publishDate, PublishTime = content.Metadata.Date ?? DateTime.Now,
UpdateTime = updateTime,
HtmlContent = content.Content HtmlContent = content.Content
}; };
@@ -191,21 +182,28 @@ public sealed partial class RendererService(
string description = builder.ToString(); string description = builder.ToString();
logger.LogDebug("Description of {name} is {desc}.", content.BlogName, logger.LogDebug("Description of {} is {}.", content.BlogName,
description); description);
return description; return description;
} }
private (uint, string) GetWordCount(BlogContent content) private uint GetWordCount(BlogContent content)
{ {
uint count = MarkdownWordCounter.CountWord(content); int count = (from c in content.Content
where char.IsLetterOrDigit(c)
select c).Count();
logger.LogDebug("Word count of {blog} is {count}", content.BlogName, logger.LogDebug("Word count of {} is {}", content.BlogName,
count); count);
// 据说语文教学大纲规定中国高中生阅读现代文的速度是600字每分钟 return (uint)count;
uint second = count / 10; }
TimeSpan span = new(0, 0, (int)second);
return (count, span.ToString("mm'分'ss'秒'")); private static string CalculateReadTime(uint wordCount)
{
// 据说语文教学大纲规定中国高中生阅读现代文的速度是600字每分钟
int second = (int)wordCount / 10;
TimeSpan span = new(0, 0, second);
return span.ToString("mm'分 'ss'秒'");
} }
} }

View File

@@ -11,13 +11,30 @@
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>net10.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <Target Name="EnsurePnpmInstalled" BeforeTargets="BeforeBuild">
<ClientAssetsRestoreCommand>pnpm install</ClientAssetsRestoreCommand> <Message Importance="low" Text="Ensure pnpm is installed..."/>
<ClientAssetsBuildCommand>pwsh build.ps1 tailwind</ClientAssetsBuildCommand> <Exec Command="pnpm --version" ContinueOnError="true">
</PropertyGroup> <Output TaskParameter="ExitCode" PropertyName="ErrorCode"/>
</Exec>
<Error Condition="$(ErrorCode) != 0" Text="Pnpm is not installed which is required for build."/>
<Message Importance="normal" Text="Installing pakages using pnpm..."/>
<Exec Command="pnpm install"/>
</Target>
<Target Name="TailwindGenerate" AfterTargets="EnsurePnpmInstalled" BeforeTargets="BeforeBuild" Condition="'$(_IsPublishing)' == 'yes'">
<Message Importance="normal" Text="Generate css files using tailwind..."/>
<Exec Command="pnpm tailwindcss -i wwwroot/tailwind.css -o $(IntermediateOutputPath)tailwind.g.css"/>
<ItemGroup>
<Content Include="$(IntermediateOutputPath)tailwind.g.css" Visible="false" TargetPath="wwwroot/tailwind.g.css"/>
</ItemGroup>
</Target>
</Project> </Project>

View File

@@ -6,4 +6,5 @@
@using static Microsoft.AspNetCore.Components.Web.RenderMode @using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop @using Microsoft.JSInterop
@using YaeBlog
@using YaeBlog.Components @using YaeBlog.Components

View File

@@ -21,6 +21,12 @@
"Link": "https://ichirinko.top", "Link": "https://ichirinko.top",
"AvatarImage": "https://ichirinko-blog-img-1.oss-cn-shenzhen.aliyuncs.com/Pic_res/img/202209122110798.png" "AvatarImage": "https://ichirinko-blog-img-1.oss-cn-shenzhen.aliyuncs.com/Pic_res/img/202209122110798.png"
}, },
{
"Name": "志田千陽",
"Description": "日出多值得",
"Link": "https://zzachary.top/",
"AvatarImage": "https://zzachary.top/img/ztqy_hub928259802d192ff5718c06370f0f2c4_48203_300x0_resize_q75_box.jpg"
},
{ {
"Name": "不会写程序的晨旭", "Name": "不会写程序的晨旭",
"Description": "一个普通大学生", "Description": "一个普通大学生",
@@ -31,7 +37,7 @@
"Name": "万木长风", "Name": "万木长风",
"Description": "世界渲染中...", "Description": "世界渲染中...",
"Link": "https://ryohai.fun", "Link": "https://ryohai.fun",
"AvatarImage": "https://ryohai.fun/static/favicons/favicon-32x32.png" "AvatarImage": "https://ryohai.fun/icon.jpg"
} }
] ]
} }

View File

@@ -1,124 +0,0 @@
#!pwsh
[cmdletbinding()]
param(
[Parameter(Mandatory = $true, Position = 0, HelpMessage = "Specify the build target")]
[ValidateSet("tailwind", "publish", "compress", "build", "dev", "new")]
[string]$Target,
[string]$Output = "wwwroot",
[string]$Essay,
[switch]$Compress
)
begin {
Write-Host "Building $Target..."
if ($Target -eq "publish")
{
if ($Essay -eq "")
{
Write-Error "No publish target, please add with --essay argument."
exit 1
}
}
if ($Target -eq "new")
{
if ($Essay -eq "")
{
Write-Error "No new name, please add with --essay argument."
exit 1
}
}
}
process {
function Compress-Image
{
Write-Host "Compress image assets..."
dotnet run -- compress --dry-run
$confirm = Read-Host "Really compress images? (y/n)"
if ($confirm -notmatch "^[yY]$")
{
Write-Host "Not compress images."
return
}
Write-Host "Do compress image..."
dotnet run -- compress
dotnet run -- scan
$confirm = Read-Host "Really delete unused images? (y/n)"
if ($confirm -notmatch "^[yY]$")
{
Write-Host "Not delete images."
return
}
Write-Host "Do delete unused images.."
dotnet run -- scan --rm
}
function Build-Image
{
$commitId = git rev-parse --short=10 HEAD
dotnet publish
podman build . -t ccr.ccs.tencentyun.com/jackfiled/blog --build-arg COMMIT_ID=$commitId
}
function Start-Develop {
Write-Host "Start tailwindcss and dotnet watch servers..."
$pnpmProcess = Start-Process pnpm "tailwindcss -i wwwroot/tailwind.css -o obj/Debug/net10.0/ClientAssets/tailwind.g.css -w" `
-PassThru
try
{
Write-Host "Started pnpm process exit? " $pnpmProcess.HasExited
Start-Process dotnet "watch -- serve" -PassThru | Wait-Process
}
finally
{
if ($pnpmProcess.HasExited)
{
Write-Error "pnpm process has exited!"
exit 1
}
Write-Host "Kill tailwindcss and dotnet watch servers..."
$pnpmProcess | Stop-Process
}
}
switch ($Target)
{
"tailwind" {
Write-Host "Build tailwind css into $Output."
pnpm tailwindcss -i wwwroot/tailwind.css -o $Output/tailwind.g.css
break
}
"publish" {
Write-Host "Publish essay $Essay..."
dotnet run -- publish $Essay
if ($Compress)
{
Compress-Image
}
break
}
"compress" {
Compress-Image
break
}
"build" {
Build-Image
break
}
"dev" {
Start-Develop
break
}
"new" {
dotnet run -- new $Essay
}
}
}

View File

@@ -1,13 +1,13 @@
version: '3.8' version: '3.8'
services: services:
blog: blog:
image: registry.cn-beijing.aliyuncs.com/jackfiled/blog:latest image: registry.cn-beijing.aliyuncs.com/jackfiled/blog:latest
restart: unless-stopped restart: unless-stopped
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
- "traefik.http.routers.blog.rule=Host(`rrricardo.top`) || Host(`www.rrricardo.top`)" - "traefik.http.routers.blog.rule=Host(`rrricardo.top`) || Host(`www.rrricardo.top`)"
- "traefik.http.services.blog.loadbalancer.server.port=8080" - "traefik.http.services.blog.loadbalancer.server.port=8080"
- "traefik.http.routers.blog.tls=true" - "traefik.http.routers.blog.tls=true"
- "traefik.http.routers.blog.tls.certresolver=myresolver" - "traefik.http.routers.blog.tls.certresolver=myresolver"
- "com.centurylinklabs.watchtower.enable=true" - "com.centurylinklabs.watchtower.enable=true"

View File

@@ -1,13 +1,12 @@
--- ---
title: High Performance Computing 25 SP NVIDIA title: High Performance Computing 25 SP NVIDIA
date: 2025-08-31T13:50:42.8639950+08:00 date: 2025-04-24T19:02:36.1077330+08:00
tags: tags:
- 高性能计算 - 高性能计算
- 学习资料 - 学习资料
--- ---
Fxxk you, NVIDIA! Fxxk you, NVIDIA!
<!--more--> <!--more-->

View File

@@ -0,0 +1,10 @@
---
title: High Performance Computing 25 SP Quantum Computing
date: 2025-06-12T19:26:24.6668760+08:00
tags:
- 高性能计算
- 学习资料
---
<!--more-->

View File

@@ -1,13 +1,11 @@
--- ---
title: High Performance Computing 25 SP Non Stored Program Computing title: High Performance Computing 2025 SP Non Stored Program Computing
date: 2025-08-31T13:51:17.5260660+08:00 date: 2025-05-29T18:29:28.6155560+08:00
tags: tags:
- 高性能计算 - 高性能计算
- 学习资料 - 学习资料
--- ---
No Von Neumann Machines. No Von Neumann Machines.
<!--more--> <!--more-->
@@ -62,7 +60,7 @@ There are two types of semi-custom ASICs:
The Standard cell based ASICs is also called as **Cell-based ASIC(CBIC)**. The Standard cell based ASICs is also called as **Cell-based ASIC(CBIC)**.
![image-20250815093113115](./hpc-2025-non-stored-program-computing/image-20250815093113115.webp) ![image-20250815093113115](./hpc-2025-non-stored-program-computing/image-20250815093113115.png)
> The *gate* is used a unit to measure the ability of semiconductor to store logical elements. > The *gate* is used a unit to measure the ability of semiconductor to store logical elements.
@@ -86,7 +84,7 @@ Depending on the structure, the standard PLD can be divided into:
- Programmable Logic Array(PLA): A programmable array of AND gates feeding a programmable of OR gates. - Programmable Logic Array(PLA): A programmable array of AND gates feeding a programmable of OR gates.
- Complex Programmable Logic Device(CPLD) and Field Programmable Gate Array(FPGA): complex enough to be called as *architecture*. - Complex Programmable Logic Device(CPLD) and Field Programmable Gate Array(FPGA): complex enough to be called as *architecture*.
![image-20250817183832472](./hpc-2025-non-stored-program-computing/image-20250817183832472.webp) ![image-20250817183832472](./hpc-2025-non-stored-program-computing/image-20250817183832472.png)
@@ -98,7 +96,7 @@ Depending on the structure, the standard PLD can be divided into:
### FPGA Architecture ### FPGA Architecture
![image-20250817184419856](./hpc-2025-non-stored-program-computing/image-20250817184419856.webp) ![image-20250817184419856](./hpc-2025-non-stored-program-computing/image-20250817184419856.png)
#### Configurable Logic Block(CLB) Architecture #### Configurable Logic Block(CLB) Architecture
@@ -118,7 +116,7 @@ LUT is a ram with data width of 1 bit and the content is programmed at power up.
The below figure shows LUT working: The below figure shows LUT working:
![image-20250817185111521](./hpc-2025-non-stored-program-computing/image-20250817185111521.webp) ![image-20250817185111521](./hpc-2025-non-stored-program-computing/image-20250817185111521.png)
The configuration memory holds the output of truth table entries, so that when the FPGA is restarting it will run with the same *program*. The configuration memory holds the output of truth table entries, so that when the FPGA is restarting it will run with the same *program*.
@@ -128,7 +126,7 @@ And as the truth table entries are just bits, the program of FPGA is called as *
Let the input signal as address, the LUT will be configured as a RAM. Normally, LUT mode performs read operations, the address decoders can generate clock signal to latches for writing operation. Let the input signal as address, the LUT will be configured as a RAM. Normally, LUT mode performs read operations, the address decoders can generate clock signal to latches for writing operation.
![image-20250817185859510](./hpc-2025-non-stored-program-computing/image-20250817185859510.webp) ![image-20250817185859510](./hpc-2025-non-stored-program-computing/image-20250817185859510.png)
#### Routing Architecture #### Routing Architecture
@@ -136,7 +134,7 @@ The logic blocks are connected to each though programmable routing network. And
Horizontal and vertical mesh or wire segments interconnection by programmable switches called programmable interconnect points(PIPs). Horizontal and vertical mesh or wire segments interconnection by programmable switches called programmable interconnect points(PIPs).
![image-20250817192006784](./hpc-2025-non-stored-program-computing/image-20250817192006784.webp) ![image-20250817192006784](./hpc-2025-non-stored-program-computing/image-20250817192006784.png)
These PIPs are implemented using a transmission gate controlled by a memory bits from the configuration memory. These PIPs are implemented using a transmission gate controlled by a memory bits from the configuration memory.
@@ -148,7 +146,7 @@ Several types of PIPs are used in the FPGA:
- Non-decoded MUX: n wire segments each with a configuration bit. - Non-decoded MUX: n wire segments each with a configuration bit.
- Compound cross-point: 6 breakpoint PIPs and can isolate two isolated signal nets. - Compound cross-point: 6 breakpoint PIPs and can isolate two isolated signal nets.
![image-20250817194355228](./hpc-2025-non-stored-program-computing/image-20250817194355228.webp) ![image-20250817194355228](./hpc-2025-non-stored-program-computing/image-20250817194355228.png)
#### Input/Output Architecture #### Input/Output Architecture
@@ -160,7 +158,7 @@ The programmable Input/Output cells consists of three parts:
- Routing resources. - Routing resources.
- Programmable I/O voltage and current levels. - Programmable I/O voltage and current levels.
![image-20250817195139631](./hpc-2025-non-stored-program-computing/image-20250817195139631.webp) ![image-20250817195139631](./hpc-2025-non-stored-program-computing/image-20250817195139631.png)
#### Fine-grained and Coarse-grained Architecture #### Fine-grained and Coarse-grained Architecture
@@ -188,9 +186,9 @@ Three types of interconnected devices have been commonly used to connect there w
### FPGA Design Flow ### FPGA Design Flow
![image-20250817195714935](./hpc-2025-non-stored-program-computing/image-20250817195714935.webp) ![image-20250817195714935](./hpc-2025-non-stored-program-computing/image-20250817195714935.png)
![image-20250817200350750](./hpc-2025-non-stored-program-computing/image-20250817200350750.webp) ![image-20250817200350750](./hpc-2025-non-stored-program-computing/image-20250817200350750.png)
The FPGA configuration techniques contains: The FPGA configuration techniques contains:
@@ -224,7 +222,7 @@ The OpenCL is not an traditional hardare description language. And OpenCL needs
The follow figure shows how the OpenCL-FPGA compiler turns an vector adding function into the circuit. The follow figure shows how the OpenCL-FPGA compiler turns an vector adding function into the circuit.
![image-20250829210329225](./hpc-2025-non-stored-program-computing/image-20250829210329225.webp) ![image-20250829210329225](./hpc-2025-non-stored-program-computing/image-20250829210329225.png)
The compiler generates three stages for this function: The compiler generates three stages for this function:

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,12 +1,11 @@
--- ---
title: High Performance Computing 25 SP OpenCL Programming title: High Performance Computing 2025 SP OpenCL Programming
date: 2025-08-31T13:51:02.0181970+08:00 date: 2025-05-29T18:29:14.8444660+08:00
tags: tags:
- 高性能计算 - 高性能计算
- 学习资料 - 学习资料
--- ---
Open Computing Language. Open Computing Language.
<!--more--> <!--more-->

View File

@@ -1,12 +1,11 @@
--- ---
title: High Performance Computing 25 SP Potpourri title: High Performance Computing 25 SP Potpourri
date: 2025-08-31T13:51:29.8809980+08:00 date: 2025-06-12T18:45:49.2698190+08:00
tags: tags:
- 高性能计算 - 高性能计算
- 学习资料 - 学习资料
--- ---
Potpourri has a good taste. Potpourri has a good taste.
<!--more--> <!--more-->

Some files were not shown because too many files have changed in this diff Show More