Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/content.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const posts = defineCollection({
date: z.coerce.date(),
author: z.string().default(DEFAULT_AUTHOR),
authorAvatar: z.url().default(DEFAULT_AVATAR),
tags: z.array(z.string()).default([]),
cover: z
.object({
src: z.url(),
Expand Down
3 changes: 3 additions & 0 deletions src/content/posts/1-21-11.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
title: "1.21.11"
date: "2025-12-21T20:00:00Z"
author: "Paper Team"
tags:
- "updates"
- "paper"
---

## The 1.21.11 Update
Expand Down
3 changes: 3 additions & 0 deletions src/content/posts/1-21.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
title: "1.21"
date: "2024-07-20T18:00:16.000Z"
author: "Paper Team"
tags:
- "updates"
- "paper"
---

## The 1.21 Update
Expand Down
2 changes: 2 additions & 0 deletions src/content/posts/welcome-to-papermc.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
title: "Welcome to PaperMC"
date: "2021-12-14T03:36:05.000Z"
author: "Paper Team"
tags:
- "paperchan"
---

Welcome to PaperMC!
Expand Down
17 changes: 16 additions & 1 deletion src/pages/news/[id].astro
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const mins = minutesToRead(words);
const excerpt = truncateForPreview(post.body ?? "", 120);
---

<Layout title={post.data.title!} description={excerpt} keywords={[]} canonical={`/news/${post.id}`}>
<Layout title={post.data.title!} description={excerpt} keywords={post.data.tags ?? []} canonical={`/news/${post.id}`}>
<section class="container mx-auto mt-10 max-w-7xl px-4 py-12">
<article>
<header class="px-4">
Expand Down Expand Up @@ -85,6 +85,21 @@ const excerpt = truncateForPreview(post.body ?? "", 120);
<div class="px-6 pb-6 text-right text-xs text-gray-500 sm:px-8 dark:text-white/50">{post.data.cover.credit}</div>
)
}

{
post.data.tags && post.data.tags.length > 0 && (
<div class="mt-8 flex flex-wrap gap-2 px-4 pb-8">
{post.data.tags.map((tag: string) => (
<a
href={`/news/tags/${tag}`}
class="bg-gray-100 px-3 py-1 text-xs font-semibold text-gray-700 transition hover:bg-gray-200 dark:bg-white/10 dark:text-white/80 dark:hover:bg-white/20"
>
#{tag}
</a>
))}
</div>
)
}
</article>
</section>
</Layout>
Expand Down
49 changes: 33 additions & 16 deletions src/pages/news/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -30,30 +30,47 @@ const postsWithMeta = posts.map((p) => {
{
postsWithMeta.map((entry, idx) => (
<a href={`/news/${entry.id}`} class="group rounded-lg">
<div class="btn btn-outline btn-gray flex flex-col gap-6 rounded-lg py-6 transition-colors duration-200">
<div class="btn btn-outline btn-gray flex flex-col gap-6 rounded-lg transition-colors duration-200">
<div class="flex flex-col gap-3 p-6">
<div class="flex items-baseline justify-between gap-4">
<h2 class="text-foreground group-hover:text-primary text-xl leading-tight font-bold text-balance">{entry.data.title}</h2>
{idx === 0 ? (
<span class="bg-channel-stable-primary text-channel-stable-secondary shadow-channel-stable-primary/50 ml-4 inline-flex items-center rounded-sm px-2.5 py-0.5 text-xs font-semibold shadow-sm">
Latest
</span>
) : null}
<div class="flex flex-wrap items-center gap-2">
{idx === 0 ? (
<span class="bg-channel-stable-primary text-channel-stable-secondary shadow-channel-stable-primary/50 ml-2 inline-flex items-center rounded-sm px-2.5 py-0.5 text-xs font-semibold shadow-sm">
Latest
</span>
) : null}
</div>
</div>

<div class="text-muted-foreground text-xs">
<div class="flex flex-row flex-wrap items-baseline gap-x-3 gap-y-1">
<span class="inline-flex items-baseline gap-1.5">
<Icon name="icons/lucide/calendar" class="size-3.5 translate-y-px" />
<span>{formatDateLong(new Date(entry.data.date))}</span>
</span>
<div class="flex flex-col gap-2">
<div class="flex flex-row flex-wrap items-baseline gap-x-3 gap-y-1">
<span class="inline-flex items-baseline gap-1.5">
<Icon name="icons/lucide/calendar" class="size-3.5 translate-y-px" />
<span>{formatDateLong(new Date(entry.data.date))}</span>
</span>

<span aria-hidden="true">·</span>

<span aria-hidden="true">·</span>
<span class="inline-flex items-baseline gap-1.5">
<Icon name="icons/lucide/clock" class="size-3.5 translate-y-px" />
<span>{entry.mins} min read</span>
</span>
</div>

<span class="inline-flex items-baseline gap-1.5">
<Icon name="icons/lucide/clock" class="size-3.5 translate-y-px" />
<span>{entry.mins} min read</span>
</span>
{entry.data.tags && entry.data.tags.length > 0 && (
<div class="flex flex-wrap gap-1.5">
{entry.data.tags.map((tag: string) => (
<a
href={`/news/tags/${tag}`}
class="rounded-full border border-gray-300 bg-gray-200 px-2 py-0.5 text-[10px] font-medium tracking-wider text-gray-800 uppercase transition-colors hover:bg-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600"
>
{tag}
</a>
))}
</div>
)}
</div>
</div>

Expand Down
80 changes: 80 additions & 0 deletions src/pages/news/tags/[tag].astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
import Layout from "@/layouts/Layout.astro";
import { getCollection } from "astro:content";
import { countWords, minutesToRead, truncateForPreview } from "@/utils/content";
import { formatDateLong } from "@/utils/time.ts";
import { Icon } from "astro-icon/components";

export async function getStaticPaths() {
const allPosts = await getCollection("posts");

const uniqueTags = [...new Set(allPosts.map((post: any) => post.data.tags).flat())];
return uniqueTags.map((tag) => {
const filteredPosts = allPosts.filter((post: any) => post.data.tags.includes(tag));
const postsWithMeta = filteredPosts.map((p) => {
const words = countWords(p.body ?? "");
const excerpt = truncateForPreview(p.body ?? "", 512);
return {
...p,
words,
mins: minutesToRead(words),
excerpt,
};
});
return {
params: { tag },
props: { posts: postsWithMeta },
};
});
}
const { tag } = Astro.params;
const { posts } = Astro.props;
---

<Layout title={tag} description="news using the tag `{tag}`" keywords={["blog", "news", "updates", "latest"]} canonical="/tags">
<div class="container mx-auto mt-10 max-w-7xl px-4 py-12">
<header class="flex justify-between">
<h1 class="text-4xl leading-normal font-medium lg:text-5xl lg:leading-normal">Posts with tag: {tag}</h1>
</header>
<div class="flex flex-col gap-4">
{
posts.map((entry, idx) => (
<a href={`/news/${entry.id}`} class="group rounded-lg">
<div class="btn btn-outline btn-gray flex flex-col gap-6 rounded-lg py-6 transition-colors duration-200">
<div class="flex flex-col gap-3 p-6">
<div class="flex items-baseline justify-between gap-4">
<h2 class="text-foreground group-hover:text-primary text-xl leading-tight font-bold text-balance">{entry.data.title}</h2>
{idx === 0 ? (
<span class="bg-channel-stable-primary text-channel-stable-secondary shadow-channel-stable-primary/50 ml-4 inline-flex items-center rounded-sm px-2.5 py-0.5 text-xs font-semibold shadow-sm">
Latest
</span>
) : null}
</div>

<div class="text-muted-foreground text-xs">
<div class="flex flex-row flex-wrap items-baseline gap-x-3 gap-y-1">
<span class="inline-flex items-baseline gap-1.5">
<Icon name="icons/lucide/calendar" class="size-3.5 translate-y-px" />
<span>{formatDateLong(new Date(entry.data.date))}</span>
</span>

<span aria-hidden="true">·</span>

<span class="inline-flex items-baseline gap-1.5">
<Icon name="icons/lucide/clock" class="size-3.5 translate-y-px" />
<span>{entry.mins} min read</span>
</span>
</div>
</div>

<p class="text-muted-foreground mask-fade-bottom max-h-28 overflow-hidden text-sm leading-relaxed [--fade-size:2.5rem]">
{entry.excerpt}
</p>
</div>
</div>
</a>
))
}
</div>
</div>
</Layout>
35 changes: 35 additions & 0 deletions src/pages/news/tags/index.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
import Layout from "@/layouts/Layout.astro";
import { getCollection } from "astro:content";
const allPosts = await getCollection("posts");
const tags = [...new Set(allPosts.map((post: any) => post.data.tags).flat())];
---

<Layout title="Tag Index" description="Shows all the tags from news" keywords={["blog", "news", "updates", "latest"]} canonical="/tags">
<section class="container mx-auto mt-10 max-w-7xl px-4 py-12">
<header class="mb-8 flex justify-between">
<h1 class="text-4xl leading-normal font-medium lg:text-5xl lg:leading-normal">Tag Index</h1>
</header>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
{
tags.sort().map((tag) => (
<a
href={`/news/tags/${tag}`}
class="btn btn-outline btn-gray group flex items-center justify-between rounded-lg p-4 transition-all duration-200 hover:scale-[1.02]"
>
<span class="text-foreground group-hover:text-primary text-lg font-bold">#{tag}</span>
<div class="flex h-6 w-6 items-center justify-center rounded-full bg-gray-200 text-[10px] font-bold text-gray-600 dark:bg-white/10 dark:text-white/60">
{allPosts.filter((post: any) => post.data.tags?.includes(tag)).length}
</div>
</a>
))
}
</div>
</section>
</Layout>

<style scoped>
@view-transition {
navigation: auto;
}
</style>
Loading