diff --git a/PancakesWeb/BlogPosts/minecraft-bedrock-linux.md b/PancakesWeb/BlogPosts/minecraft-bedrock-linux.md new file mode 100644 index 0000000..a6c87d6 --- /dev/null +++ b/PancakesWeb/BlogPosts/minecraft-bedrock-linux.md @@ -0,0 +1,157 @@ +--- +title: "Minecraft: Bedrock Edition on Waydroid" +description: "A guide to running Minecraft: Bedrock Edition on Linux with Waydroid" +published: 2024-02-29 +edited: 2024-04-24T15:03:00.000+10:00 +footer: cc_by +header: header.webp +header_alt: "neowofetch of my PC with Minecraft: Bedrock Edition in the background" +header_caption: "Minecraft: Bedrock Edition running on my PC" +--- + +## Introduction + +Minecraft: Bedrock Edition is Microsoft's cross-platform version of the game. With the introduction of the Better Together Update it allowed players to play together regardless of platform through Xbox. Bedrock boasts its cross-play support for Windows, Xbox, Nintendo Switch, PlayStation, and mobile. However, macOS and Linux support are notably absent from Bedrock. While I am not aware of a workaround for macOS you can play Bedrock on Linux using [Waydroid](https://waydro.id/). This solution runs fairly well and works with cross-play because it's just *special* Android. + +## Prerequisites + +The following things are required to play Bedrock through this method: + +- A computer running Linux and systemd + - If you're running Linux but don't know what systemd is you're probably fine :3 +- A Wayland desktop environment / compositor + - GNOME + - KDE Plasma + - Sway + - river (what I'm using for this) + - COSMIC (Epoch, not tweaked GNOME) +- A graphics card compatible with Mesa + - AMD Radeon + - Intel + - NVIDIA with Nouvou+NVK (untested) +- A Google account that owns Minecraft for Android on the Play Store + +## Installation + +Before setting up Waydroid there are some distribution specific steps you must follow. + +### Arch Linux + +You will need an AUR helper or know how to manually install AUR packages. This page assumes you are using paru. You either need the binder_linux DKMS modules or the Linux Zen kernel. + +#### binder_linux + +To install the binder_linux DKMS modules install the binder_linux-dkms package from the AUR: + +```bash +paru -S binder_linux-dkms +``` + +#### Linux Zen + +To install the Linux Zen kernel run the following command: + +```bash +sudo pacman -S linux-zen linux-zen-headers +``` + +You will also need to change your system to boot with Zen. + +#### Waydroid AUR + +You can then install Waydroid from the AUR: + +```bash +paru -S waydroid +``` + +### Debian/Ubuntu + +Install Waydroid from the offficial repository: + +```bash +curl https://repo.waydro.id | sudo bash +sudo apt install waydroid +``` + +### Fedora + +Install the Waydroid package: + +```bash +sudo dnf install waydroid +``` + +### NixOS + +In `/etc/nixos/configuration.nix` or in your flake add the following and rebuild: + +```nix +virtualisation.waydroid.enable = true; +``` + +### Other + +View the [install instructions](https://docs.waydro.id/usage/install-on-desktops) for your distribution. + +## Waydroid Setup + +First you need to enable and start the `waydroid-container` service: + +```bash +sudo systemctl enable --now waydroid-container +``` + +Once the container has started you can initialize Waydroid with Google apps: + +```bash +sudo waydroid init -s GAPPS +``` + +To install apps from Google Play you must "certify" your Waydroid installation: + +```bash +waydroid session start +sudo waydroid shell +ANDROID_RUNTIME_ROOT=/apex/com.android.runtime ANDROID_DATA=/data ANDROID_TZDATA_ROOT=/apex/com.android.tzdata ANDROID_I18N_ROOT=/apex/com.android.i18n sqlite3 /data/data/com.google.android.gsf/databases/gservices.db "select * from main where name = \"android_id\";" +``` + +Copy the ID from the command output and enter it on the [Google Device registration](https://www.google.com/android/uncertified) page. Then restart the `waydroid-container` service: + +```bash +sudo systemctl restart waydroid-container +``` + +## Configure Waydroid + +Waydroid has a few settings you can configure from the command line. The one you will most likely want to set is the resolution, especially if you use fractional scaling. Waydroid currently doesn't appear to properly support fractional scaling so you will need to account for that when setting the resolution. To calculate the scaled resolution divide your display's resolution by the scaling factor, for 1920×1080 at 150% that's 1920 / 1.5 = 1280 and 1080 / 1.5 = 720. To set the resolution to 1280×720 run: + +```bash +waydroid prop set persist.waydroid.width "1280" +waydroid prop set persist.waydroid.height "720" +``` + +After changing any properties you need to restart `waydroid-container`: + +```bash +sudo systemctl restart waydroid-container +``` + +Some extra settings you might want to change in Android's System Settings are: + +- Sound > Media volume: 100% + - This will make the game audio match your system volume +- System > Language & input > Physical keyboard > Use on-screen keyboard: Off + - This prevents Android's on-screen keyboard from appearing since you probably have a physical keyboard + +## Play Minecraft + +Open Waydroid's full system UI and navigate to the Google Play Store. Sign in with your Google account if you aren't already. Search for and download Minecraft. + +You should be able to launch Minecraft from your desktop environment's application launcher or from the Waydroid full system UI. Some caveats to this solution are that render distance is limited to 32 chunks and you can't enable RTX even if you get it working on NVIDIA. Keyboard and mouse work out of the box and you may be able to pass through a controller but I couldn't get it to work in my limited testing. + +This video shows Minecraft: Bedrock Edition running with 32 chunks through Waydroid on NixOS (the video is much older than the page header) with river. + + diff --git a/PancakesWeb/BlogPosts/misskey-comparison.md b/PancakesWeb/BlogPosts/misskey-comparison.md new file mode 100644 index 0000000..b5843eb --- /dev/null +++ b/PancakesWeb/BlogPosts/misskey-comparison.md @@ -0,0 +1,635 @@ +--- +title: Comparison of Misskey Forks +description: The Misskey family of Fediverse servers is quite large. While they all share a common origin there are some notable feature differences between them. +published: 2024-02-02 +edited: 2025-02-10T19:24:00.000+10:00 +footer: cc_by +header: banner.webp +header_alt: The Misskey wordmark in black on a white background with green circles +header_caption: Image from Misskey Hub, licensed under CC BY-SA 4.0. +--- + +This page aims to provide a rough timeline of each notable fork and a comparison of their features. The feature comparisons are for the latest stable releases of stable forks and development releases of developing/unmainted forks. If anything on this page is incorrect or out of date I'd appreciate if you could let me know: [@pancakes@meow.company](https://shrimp.meow.company/@pancakes). + +## Featured Forks + +| Icon | Name | Version | Account | Website | Repository | +|-----------------------------------------------------------------------------|----------------|------------------|--------------------------------------------------------------------|-------------------------------|-----------------------------------------------------| +| ![Misskey icon](/post-assets/misskey-comparison/misskey.png){.inline-icon} | Misskey | 2024.11.0 | None | | | +| ![FoundKey icon](/post-assets/misskey-comparison/foundkey.svg){.inline-icon} | FoundKey | v13.0.0-preview6 | None | None | | +| ![Firefish icon](/post-assets/misskey-comparison/firefish.svg){.inline-icon} | Firefish | v20241205 | [@firefish@info.firefish.dev](https://info.firefish.dev/@firefish) | None | | +| ![Iceshrimp icon](/post-assets/misskey-comparison/iceshrimp.png){.inline-icon} | Iceshrimp (JS) | v2023.12.11 | None | None | | +| ![Sharkey icon](/post-assets/misskey-comparison/sharkey.png){.inline-icon} | Sharkey | 2024.11.2 | [@Sharkey@sharkey.team](https://sharkey.team/@Sharkey) | | | + +### Iceshrimp + +In previous versions of this page I referred to Iceshrimp (JS) as just "Iceshrimp". Iceshrimp (JS) is currently under a feature freeze as most of the development effort is going towards the rewrite, Iceshrimp.NET. However, Iceshrimp (JS) is not unmaintained and still receives security updates. Please be aware that when the timeline says "Iceshrimp" it is referring to Iceshrimp (JS), not Iceshrimp.NET. Iceshrimp.NET will not be included on this page as it is not a fork of Misskey. For more information about Iceshrimp (JS) and Iceshrimp.NET see the README in the repository linked above. + +### Firefish + +On 2024-09-05 Firefish entered maintenance mode and will reach end-of-support at the end of 2024. Server admins may want to downgrade to `20240206/1.0.5-rc` and migrate to another *key fork. See the [announcement post](https://info.firefish.dev/notes/9xsukr38m3komd63) for more info. + +## Timeline of Events + +The following chart is an approximate history of ActivityPub and Misskey. As well as the forks, their versioning, and differing states of activity. + +```plaintext +2016-01-28 ActivityPub W3C First Public Working Draft + +2017-02-06 Mastodon v1.0 (included for reference) + +2018-04-19 Misskey v0.0.5018 + | + | (peace in the Misskeyverse) + | +2022-06-13 Misskey v12.111.1 ------------| (fork) + | | +2022-07-19 Misskey v12.117.1 | + | | (fork) | +2022-07-20 | Calckey v12.117.1-calc | + | | | +2022-08-05 | | FoundKey v13.0.0-preview1 + | | | +2023-06-05 | | FoundKey v13.0.0-preview6 + | | | (unmaintained) + | | +2023-06-25 | Calckey v14.0.0-rc3 ----| (rebrand) + | | +2023-07-20 | Firefish v1.0.0, v1.0.1 -| (fork) + | (version scheme change) | | +2023-09-14 | | Iceshrimp v2023.09.13-rc1 + | | | +2023-09-21 Misskey 2023.9.0-beta.10 | | + | | (fork) | | +2023-10-07 | Sharkey 2023.9.1.beta4 | | + | | | | +2023-11-14 | | | Iceshrimp v2023.12.1 + | | | | | (fork) +2023-11-28 | | Firefish v1.0.5-rc | | + | | | (unmaintained) | | +2024-01-09 | | | | Catodon 24.01-dev + | | | (transferred/revived) | | +2024-02-06 | | Firefish v20240206 | | + | | | | | +2024-09-05 | | | (enter maintenance) | | + | | | | | +2024-12-31 | | | (end-of-support) | | + | | | | +``` + +## Feature Comparison + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Comparison of features between Misskey forks
MisskeyFoundKeyFirefishIceshrimp (JS)Sharkey
StatusStableUnmaintainedUnmaintainedMaintenance modeStable
General
Authorized FetchNoNoYesYesYes
Full Text SearchBuilt-in or MeiliSearchElasticSearchSonic, MeiliSearch, or ElasticSearchBuilt-inBuilt-in or MeiliSearch
Push NotificationsYesYesYesYesYes
TutorialYesYesYesYesYes
Integration
DiscordNoNoYesYesNo
GitHubNoNoYesYesNo
Mastodon APINoNoPartialYesYes
TensorFlowYesNoYesNoNo
WebhookYesYesYesYesYes
Timelines
AntennasYesYesYesYesYes
Bubble/RecommendedNoNoYesYesYes
ChannelsYesYesYesYesYes
SocialYesNoYesYesYes
User ListsYesYesYesYesYes
Posts
FederationYesYesYesYesYes
Editing & HistoryRemoved in 2023.10.0View onlyEditing onlyEditing onlyYes
Favorite/Like ButtonNoNoYesYesYes
Post LanguageNoNoYesNoNo
Module PlayerNoYesYesNoYes
Boost VisibilityNoNoYesYesYes
Report PostsYesYesYesYesYes
Markdown
Misskey Flavoured Markdown (MFM)YesYesYesYesYes
MFM borderYesNoNoYesYes
MFM center<center> only<center> onlyYesYes<center> only
MFM cropNoNoYesYesYes
MFM fadeNoNoYesYesYes
MFM followmouseNoNoNoYesYes
Per Note MFM Animation ToggleNoNoNoYesYes
Markdown ListsNoNoNoNoNo
Math FormulasNoNoYesYesYes
Users
Avatar DecorationsNot federatedNoNoNoNot federated
Profile BackgroundNoNoNoNoYes
Cat Mode & Speak as CatCat Mode onlyCat Mode onlyYesYesYes
Link VerificationYesNoYesYesYes
RolesYesNoNoNoYes
ListenBrainz StatusNoNoNoNoYes
FeedsYesYesYesYesYes
ClipsYesYesYesYesYes
GalleryYesYesYesYesYes
PagesYesYesYesYesYes
Post BackfillNoNoYesYesYes
Mute UsersYesYesYesYesYes
Block UsersYesYesYesYesYes
Report UsersYesYesYesYesYes
Disable Post IndexingNoNoYesYesYes
Import/ExportAll notes, Favorite notes, Followed users, User lists, Muted users, Blocked + users, and AntennasAll posts, Followed users, User lists, Muted users, and Blocked usersAll posts, Followed users, User lists, Muted users, and Blocked usersAll posts, Followed users, User lists, Muted users, and Blocked usersAll notes, Favorite notes, Followed users, User lists, Muted users, Blocked + users, and Antennas
Post ImportNoNoMastodon+NoMastodon+, Pleroma, Akkoma, Misskey+, Firefish+, Twitter, Instagram, and + Facebook
GDPR Data Subject Access RequestsNoNoNoNoYes
MigrationYesReceive onlyYesYesYes
Moderation
Approve UsersNoNoNoNoYes
Silence UsersYesNoYesYesYes
Mark User Media NSFWNoNoNoYesYes
+ +## Glossary + +Brief descriptions of each feature listed above. I'm keeping them vague as they vary based on software and more broadly across the Fediverse. + +### Legends + +- **\[Software\]+:** Software plus forks. + +### General + +- **Authorized Fetch:** A security feature which requires remote instances to identify themselves when requesting information from the local instance. This makes it more difficult for remote instances to access information that they are not permitted to. [Read more](https://docs.joinmastodon.org/admin/config/#authorized_fetch) +- **Full Text Search:** Allows users to perform more advanced post searches. This may include filters for things as the user that made the post or if the post has an image. +- **Push Notifications:** Allows users to receive notifications when the website is not open. + +### Integration + +- **Mastodon API:** The API used by Mastodon. Allows users to use apps/clients that were made for Mastodon. +- **TensorFlow:** Mainly used by Misskey+ to automatically detect NSFW content. + +### Timelines + +- **Antennas:** One or more timelines that only show posts that fit certain criteria/filters. +- **Bubble/Recommended:** A timeline that only shows posts from instances that the admin manually approves. +- **Social:** Combined Home and Local timeline. +- **User Lists:** One or more timelines that only show users that are added to the timeline. Note: If you receive a follow request from an account named `@instance.actor@...` it most likely means a user from that instance that doesn't follow you is trying to add you to a list. + +### Posts + +- **Editing & History:** Allows users to make changes to their posts and to see a history of changes. +- **Module Player:** Allows users to upload and listen to module or tracker files. +- **Boost Visibility:** Allows users to change the visibility of their boosts between Public, Unlisted, Followers-only, and Local. + +### Markdown + +- **Misskey Flavoured Markdown (MFM):** A flavor of Markdown created by Misskey which allows for special formatting such as scale, text color, animation, and more. +- **MFM fade:** An animation for fading in and out content. +- **Per Note MFM Animation Toggle:** By default MFM animations are disabled. Posts with MFM animations will have a play/pause button that is initially paused. + +### Users + +- **Avatar Decorations:** "Stickers" that can be placed and resized on your profile picture. +- **Cat Mode & Speak as Cat:** Cat Mode adds cat ears to the user's profile picture. Speak as Cat modifies the appearance of posts to be more cat-like. For example "everyone" becomes "everynyan". If Speak as Cat is not a feature of the software then it is included as part of Cat Mode. This feature is taken *very seriously*. +- **Link Verification:** Verified links will show a checkmark next to them. This can help to verify that a user is who they say they are. [How to verify links](https://docs.joinmastodon.org/user/profile/#verification") +- **Roles:** Allows admins to control what certain groups of users can and can't do. Displays a role badge on the user profile. +- **ListenBrainz Status:** Displays what song the user is listening to. Similar to Last.fm. +- **Feeds:** Automatically generate RSS, Atom, and JSON feeds for public posts from public accounts. +- **Pages:** Allows users to attach documents to their profile with extra functionality compared to posts. +- **Post Backfill:** Older posts from remote users will be automatically downloaded to the local instance. +- **Disable Post Indexing:** Prevent posts from being indexed by search engines. +- **Import/Export:** Upload and download personal data to move it between accounts. +- **Post Import:** Allows the user to upload posts that were exported from other platforms. This copies the original post dates and may import attached media. +- **Migration/Move:** Move followers from an old account to a new account. This does not move followed users or other data, see: Import/Export. + +### Moderation + +- **Approve Users:** If enabled users will need to provide a reason why they are registering. Moderators can then approve or deny a user's account registration. +- **Mark User Media NSFW:** Allows moderators to mark some or all of a user's media as Not Safe For Work. + +## Special Thanks + +### Corrections + +- [@Amelia@transfem.social](https://transfem.social/@Amelia") +- [@blueb@eepy.zone](https://eepy.zone/@blueb") +- [@frost@wolfdo.gg](https://wolfdo.gg/@frost") +- [@jeder@miau.jeder.pl](https://miau.jeder.pl/@jeder") +- [@jegler@heckin.how](https://heckin.how/@jegler") +- [@lewdum@heckin.how](https://heckin.how/@lewdum") +- [@Marie@dev.joinsharkey.org](https://dev.joinsharkey.org/@Marie") +- [@netbat@catodon.social](https://catodon.social/@netbat") +- [@privateger@plasmatrap.com](https://plasmatrap.com/@privateger") +- [@vavency@kitsunes.club](https://kitsunes.club/@vavency") +- [@Weeble@bungle.online](https://bungle.online/@Weeble") + + diff --git a/PancakesWeb/Components/Layout/MainLayout.razor b/PancakesWeb/Components/Layout/MainLayout.razor index 2eceb99..69c689b 100644 --- a/PancakesWeb/Components/Layout/MainLayout.razor +++ b/PancakesWeb/Components/Layout/MainLayout.razor @@ -6,6 +6,7 @@
+
@Body diff --git a/PancakesWeb/Components/Pages/BlogPost.razor b/PancakesWeb/Components/Pages/BlogPost.razor new file mode 100644 index 0000000..80b3787 --- /dev/null +++ b/PancakesWeb/Components/Pages/BlogPost.razor @@ -0,0 +1,91 @@ +@page "/{Slug}" +@using Microsoft.AspNetCore.Components.Sections + +@if (Post != null) +{ + + @if (Post.HeaderFilename != null) + { +
+ @Post.HeaderAlt + @if (Post.HeaderCaption != null) + { +
@Post.HeaderCaption
+ } +
+ } +
+

@Post.Title

+

@Post.Description

+ @if (Post.Edited != null) + { + var edited = Post.Edited ?? DateTime.Now; // this null check should never happen + + Posted: + • + Latest edit: + + } + else + { + Posted: + } +
+
+
+} +else +{ +
+

Not found

+

Page not found

+ Go home +
+ + return; +} + +@if (Post?.Content != null) +{ +
+ @((MarkupString)Post.Content) +
+} + +@if (Post?.Footer != null) +{ + + @switch (Post.Footer) + { + case BlogPosts.PostFooter.CcBy: +

+ @Post.Title © @Post.Published.Year by + pancakes is licensed under + CC BY 4.0 + CC + BY +

+ break; + default: + break; + } +
+} + +@code { + [Inject] private NavigationManager Nav { get; set; } = null!; + + [Parameter] public required string Slug { get; set; } + + public BlogPosts.Post? Post { get; set; } + + protected override void OnInitialized() + { + if (string.IsNullOrWhiteSpace(Slug)) + { + return; + } + + Post = BlogPosts.Posts.FirstOrDefault(p => p.Slug == Slug); + } +} \ No newline at end of file diff --git a/PancakesWeb/Components/Pages/BlogPost.razor.css b/PancakesWeb/Components/Pages/BlogPost.razor.css new file mode 100644 index 0000000..2b1b3c9 --- /dev/null +++ b/PancakesWeb/Components/Pages/BlogPost.razor.css @@ -0,0 +1,29 @@ +.header { + max-width: var(--container-width); + margin-left: auto; + margin-right: auto; +} + +.header-img { + aspect-ratio: 21/9; + width: 100%; + object-fit: cover; +} + +header .container { + padding-top: 0; + padding-bottom: 0; + + font-size: large; + line-height: 1.4; +} + +article { + padding-top: 0; +} + +@media screen and (min-width: 768px) { + .header-img { + border-radius: var(--radius); + } +} \ No newline at end of file diff --git a/PancakesWeb/Components/Pages/Home.razor b/PancakesWeb/Components/Pages/Home.razor index 123064a..ce51909 100644 --- a/PancakesWeb/Components/Pages/Home.razor +++ b/PancakesWeb/Components/Pages/Home.razor @@ -39,7 +39,7 @@

Blog Posts

Things I've written. Licensing can be found in the appropriate page footers

- @* TODO: Add blog index *@ +

Projects

Projects that I have created

diff --git a/PancakesWeb/Components/UI/BlogPosts.razor b/PancakesWeb/Components/UI/BlogPosts.razor new file mode 100644 index 0000000..5fad54b --- /dev/null +++ b/PancakesWeb/Components/UI/BlogPosts.razor @@ -0,0 +1,22 @@ +
    + @foreach (var post in Posts) + { +
  1. + @post.Title +

    @post.Description

    + @if (post.Edited != null) + { + var edited = post.Edited ?? DateTime.Now; // this null check should never happen + + Posted: + • + Edited: + + } + else + { + Posted: + } +
  2. + } +
\ No newline at end of file diff --git a/PancakesWeb/Components/UI/BlogPosts.razor.cs b/PancakesWeb/Components/UI/BlogPosts.razor.cs new file mode 100644 index 0000000..75c0544 --- /dev/null +++ b/PancakesWeb/Components/UI/BlogPosts.razor.cs @@ -0,0 +1,109 @@ +using Markdig; +using Markdig.Extensions.AutoLinks; +using Markdig.Extensions.Yaml; +using Markdig.Renderers; +using Markdig.Syntax; +using Microsoft.AspNetCore.Components; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace PancakesWeb.Components.UI; + +public partial class BlogPosts : ComponentBase +{ + public static List Posts = new(); + + static BlogPosts() + { + foreach (var file in Directory.EnumerateFiles("BlogPosts")) + { + if (file.EndsWith(".md")) + { + Posts.Add(new Post(Path.GetFileNameWithoutExtension(file))); + } + } + + Posts = Posts.OrderByDescending(p => p.Published).ToList(); + } + + public class Post + { + public readonly string Slug; + public string Content = null!; + + public string Title = null!; + public string Description = null!; + public DateOnly Published = DateOnly.MinValue; + public DateTime? Edited; + public PostFooter? Footer; + public string? HeaderFilename; + public string? HeaderAlt; + public string? HeaderCaption; + + public Post(string slug) + { + Slug = slug; + UpdatePost(); + } + + public void UpdatePost() + { + var text = File.ReadAllText($"BlogPosts/{Slug}.md"); + var (metadata, html) = ParseMarkdown(text); + + Content = html; + Title = metadata.Title; + Description = metadata.Description; + Published = metadata.Published; + Edited = metadata.Edited; + Footer = metadata.Footer; + HeaderFilename = metadata.Header; + HeaderAlt = metadata.HeaderAlt; + HeaderCaption = metadata.HeaderCaption; + } + + private static (PostMetadata metadata, string html) ParseMarkdown(string markdown) + { + var pipeline = new MarkdownPipelineBuilder().UseAbbreviations().UseAlertBlocks().UseAutoIdentifiers() + .UseAutoLinks(new AutoLinkOptions { OpenInNewWindow = true }).UseEmphasisExtras().UseFootnotes() + .UseGenericAttributes().UseMediaLinks().UsePipeTables().UseSmartyPants().UseTaskLists() + .UseYamlFrontMatter().Build(); + var writer = new StringWriter(); + var renderer = new HtmlRenderer(writer); + pipeline.Setup(renderer); + + var document = Markdown.Parse(markdown, pipeline); + + var yaml = ""; + var frontMatter = document.Descendants().FirstOrDefault(); + if (frontMatter != null) + yaml = markdown.Substring(frontMatter.Span.Start + 4, frontMatter.Span.Length - 8); + + renderer.Render(document); + writer.Flush(); + + var deserializer = new DeserializerBuilder().WithNamingConvention(UnderscoredNamingConvention.Instance) + .WithEnumNamingConvention(UnderscoredNamingConvention.Instance).Build(); + var metadata = deserializer.Deserialize(yaml); + + return (metadata, writer.ToString()); + } + } + + private class PostMetadata + { + public string Title { get; set; } = null!; + public string Description { get; set; } = null!; + public string? Header { get; set; } + public string? HeaderAlt { get; set; } + public string? HeaderCaption { get; set; } + public PostFooter? Footer { get; set; } + public DateOnly Published { get; set; } + public DateTime? Edited { get; set; } + } + + public enum PostFooter + { + CcBy + } +} \ No newline at end of file diff --git a/PancakesWeb/Components/UI/BlogPosts.razor.css b/PancakesWeb/Components/UI/BlogPosts.razor.css new file mode 100644 index 0000000..9780559 --- /dev/null +++ b/PancakesWeb/Components/UI/BlogPosts.razor.css @@ -0,0 +1,12 @@ +.posts { + display: flex; + flex-direction: column; + gap: 1rem; + padding-left: 0; + list-style: none; +} + +.post-heading { + font-size: large; + font-weight: bold; +} \ No newline at end of file diff --git a/PancakesWeb/Components/UI/Navbar.razor.css b/PancakesWeb/Components/UI/Navbar.razor.css index fa651c5..9932852 100644 --- a/PancakesWeb/Components/UI/Navbar.razor.css +++ b/PancakesWeb/Components/UI/Navbar.razor.css @@ -51,6 +51,10 @@ a::after { } @media screen and (max-width: 768px) { + .nav-content { + margin-bottom: 1rem; + } + /* Hide labels while still being accessible by screen readers */ .nav-link.can-hide-label { gap: 0; diff --git a/PancakesWeb/Components/UI/RenderDate.razor b/PancakesWeb/Components/UI/RenderDate.razor deleted file mode 100644 index 8ea4ec8..0000000 --- a/PancakesWeb/Components/UI/RenderDate.razor +++ /dev/null @@ -1,7 +0,0 @@ - - -@code { - [Parameter, EditorRequired] public required DateTime Date { get; set; } -} \ No newline at end of file diff --git a/PancakesWeb/Components/UI/RenderDateOnly.razor b/PancakesWeb/Components/UI/RenderDateOnly.razor new file mode 100644 index 0000000..c4dc26e --- /dev/null +++ b/PancakesWeb/Components/UI/RenderDateOnly.razor @@ -0,0 +1,7 @@ + + +@code { + [Parameter, EditorRequired] public required DateOnly Dateonly { get; set; } +} \ No newline at end of file diff --git a/PancakesWeb/Components/UI/RenderDateTime.razor b/PancakesWeb/Components/UI/RenderDateTime.razor new file mode 100644 index 0000000..76e0b6c --- /dev/null +++ b/PancakesWeb/Components/UI/RenderDateTime.razor @@ -0,0 +1,7 @@ + + +@code { + [Parameter, EditorRequired] public required DateTime Datetime { get; set; } +} \ No newline at end of file diff --git a/PancakesWeb/Components/UI/RenderTimeOnly.razor b/PancakesWeb/Components/UI/RenderTimeOnly.razor new file mode 100644 index 0000000..6a1ead8 --- /dev/null +++ b/PancakesWeb/Components/UI/RenderTimeOnly.razor @@ -0,0 +1,7 @@ + + +@code { + [Parameter, EditorRequired] public required TimeOnly Timeonly { get; set; } +} \ No newline at end of file diff --git a/PancakesWeb/PancakesWeb.csproj b/PancakesWeb/PancakesWeb.csproj index 5a25aeb..13d4c41 100644 --- a/PancakesWeb/PancakesWeb.csproj +++ b/PancakesWeb/PancakesWeb.csproj @@ -6,4 +6,9 @@ enable + + + + + diff --git a/PancakesWeb/wwwroot/post-assets/minecraft-bedrock-linux/demo.webm b/PancakesWeb/wwwroot/post-assets/minecraft-bedrock-linux/demo.webm new file mode 100644 index 0000000..39d6129 Binary files /dev/null and b/PancakesWeb/wwwroot/post-assets/minecraft-bedrock-linux/demo.webm differ diff --git a/PancakesWeb/wwwroot/post-assets/minecraft-bedrock-linux/header.webp b/PancakesWeb/wwwroot/post-assets/minecraft-bedrock-linux/header.webp new file mode 100644 index 0000000..25ad8a9 Binary files /dev/null and b/PancakesWeb/wwwroot/post-assets/minecraft-bedrock-linux/header.webp differ diff --git a/PancakesWeb/wwwroot/post-assets/misskey-comparison/banner.webp b/PancakesWeb/wwwroot/post-assets/misskey-comparison/banner.webp new file mode 100644 index 0000000..c6442fd Binary files /dev/null and b/PancakesWeb/wwwroot/post-assets/misskey-comparison/banner.webp differ diff --git a/PancakesWeb/wwwroot/post-assets/misskey-comparison/catodon.ico b/PancakesWeb/wwwroot/post-assets/misskey-comparison/catodon.ico new file mode 100644 index 0000000..0a2ac43 Binary files /dev/null and b/PancakesWeb/wwwroot/post-assets/misskey-comparison/catodon.ico differ diff --git a/PancakesWeb/wwwroot/post-assets/misskey-comparison/firefish.svg b/PancakesWeb/wwwroot/post-assets/misskey-comparison/firefish.svg new file mode 100644 index 0000000..7b36f4d --- /dev/null +++ b/PancakesWeb/wwwroot/post-assets/misskey-comparison/firefish.svg @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PancakesWeb/wwwroot/post-assets/misskey-comparison/foundkey.svg b/PancakesWeb/wwwroot/post-assets/misskey-comparison/foundkey.svg new file mode 100644 index 0000000..fec1e0b --- /dev/null +++ b/PancakesWeb/wwwroot/post-assets/misskey-comparison/foundkey.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/PancakesWeb/wwwroot/post-assets/misskey-comparison/iceshrimp.png b/PancakesWeb/wwwroot/post-assets/misskey-comparison/iceshrimp.png new file mode 100644 index 0000000..175adfd Binary files /dev/null and b/PancakesWeb/wwwroot/post-assets/misskey-comparison/iceshrimp.png differ diff --git a/PancakesWeb/wwwroot/post-assets/misskey-comparison/misskey.png b/PancakesWeb/wwwroot/post-assets/misskey-comparison/misskey.png new file mode 100644 index 0000000..042553e Binary files /dev/null and b/PancakesWeb/wwwroot/post-assets/misskey-comparison/misskey.png differ diff --git a/PancakesWeb/wwwroot/post-assets/misskey-comparison/sharkey.png b/PancakesWeb/wwwroot/post-assets/misskey-comparison/sharkey.png new file mode 100644 index 0000000..cacb892 Binary files /dev/null and b/PancakesWeb/wwwroot/post-assets/misskey-comparison/sharkey.png differ diff --git a/PancakesWeb/wwwroot/style.css b/PancakesWeb/wwwroot/style.css index ce1d315..6f3b8ee 100644 --- a/PancakesWeb/wwwroot/style.css +++ b/PancakesWeb/wwwroot/style.css @@ -90,7 +90,18 @@ details[open] { padding-bottom: 1rem; } +article { + font-size: large; + line-height: 1.4; +} + +hr { + color: var(--border-color); +} + code { + padding: 0.1em 0.2em; + font-family: var(--font-mono), monospace; background-color: var(--foreground); border: 1px solid var(--border-color); @@ -107,6 +118,25 @@ pre code { overflow-x: auto; text-wrap: nowrap; + line-height: 1; +} + +figcaption { + text-align: center; +} + +img, video { + max-width: 100%; +} + +table { + display: block; + max-width: 100%; + overflow-x: auto; +} + +table:not(.no-rows) tbody tr:nth-child(2n-1) { + background-color: var(--foreground); } button, input[type=submit] { @@ -186,6 +216,12 @@ pre code:hover + .copy-code-button, .copy-code-button:hover { color: var(--error); } +.inline-icon { + display: inline-block; + vertical-align: middle; + height: 1.5em; +} + @keyframes title-fade-in { 0% { margin-top: 0;