Compare commits

..

2 commits

Author SHA1 Message Date
grassblock
458dc2dafe feat: multi-authors support 2025-05-23 22:03:54 +08:00
grassblock
af55568839 fix: redundant 'true' at footer when no javascript is disabled 2025-05-23 21:59:47 +08:00
9 changed files with 55 additions and 9 deletions

View file

@ -1,4 +1,5 @@
--- ---
const { title } = Astro.props; import {siteConfig} from "../config";
const { title, email = siteConfig.defaultAuthor.email } = Astro.props;
--- ---
<a href={`mailto:test@email.com?subject=RE:${title}&body=Hi,\n\nI would like to reply to your post "${title}".`}>&#x21A9; Reply via Email</a> <a href={`mailto:${email}?subject=RE:${title}&body=Hi,\n\nI would like to reply to your post "${title}".`}>&#x21A9; Reply via Email</a>

View file

@ -1,9 +1,17 @@
--- ---
const { title, description, ogImage = "" } = Astro.props interface Props {
title: string;
description: string;
author?: string;
ogImage?: string;
}
const { title, author,description, ogImage = "" } = Astro.props
const canonicalURL = new URL(Astro.url.pathname, Astro.site); const canonicalURL = new URL(Astro.url.pathname, Astro.site);
--- ---
<meta name="title" content={title} /> <meta name="title" content={title} />
{author && <meta name="author" content={author} />}
<meta name="description" content={description} /> <meta name="description" content={description} />
{/* Open Graph / Facebook */} {/* Open Graph / Facebook */}
<meta name="og:title" content={title} /> <meta name="og:title" content={title} />

View file

@ -3,6 +3,11 @@ export const siteConfig = {
title: '/var/log/mercury', title: '/var/log/mercury',
description: 'A blog about software development, technology, and life.', description: 'A blog about software development, technology, and life.',
homepageOgImage: '', homepageOgImage: '',
defaultAuthor: {
id: 'd6e4661d', // (optional) an id in the authors.yaml, will override the setting below (if id exists)
name: 'GrassBlock1',
email: 'hi@mercury.info',
},
// features // features
noClientJavaScript: false, // disable client-side javascript, this will: noClientJavaScript: false, // disable client-side javascript, this will:
// 1. disable all built-in client-side javascript from rendering // 1. disable all built-in client-side javascript from rendering
@ -10,7 +15,6 @@ export const siteConfig = {
// 3. the comments will be globally disabled // 3. the comments will be globally disabled
// 4. the night mode & back to top will not use Javascript to function // 4. the night mode & back to top will not use Javascript to function
// 5. the neko will be force-disabled // 5. the neko will be force-disabled
authorDefaultEmail: '',
// site components // site components
navBarItems: [ navBarItems: [
// additional items in the navbar // additional items in the navbar

View file

@ -3,6 +3,7 @@ import {posts} from './posts/_schemas';
import {pages} from "./pages/_schemas"; import {pages} from "./pages/_schemas";
import {file} from 'astro/loaders'; import {file} from 'astro/loaders';
import { z } from 'astro:content'; import { z } from 'astro:content';
import {siteConfig} from "../config.ts";
const blogCollection = defineCollection({ const blogCollection = defineCollection({
type: 'content', type: 'content',
@ -21,8 +22,21 @@ const blogRollData = defineCollection({
}) })
}); });
const authorsData = defineCollection({
loader: file("src/data/authors.yaml"),
schema: z.object({
name: z.string().default(siteConfig.defaultAuthor.name),
email: z.string().email().default(siteConfig.defaultAuthor.email),
social: z.object({
twitter: z.string().optional(),
fediverse: z.string().optional(),
}).optional(),
})
});
export const collections = { export const collections = {
'posts': blogCollection, 'posts': blogCollection,
'pages': pageCollection, 'pages': pageCollection,
'links': blogRollData, 'links': blogRollData,
'authors': authorsData,
}; };

View file

@ -6,4 +6,5 @@ export const posts = ({ image }) => z.object({
pubDate: z.coerce.date(), pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(), updatedDate: z.coerce.date().optional(),
cover: image().optional(), cover: image().optional(),
author: z.string().optional(),
}); });

View file

@ -2,6 +2,7 @@
title: 'The Art of Minimalism' title: 'The Art of Minimalism'
description: 'Thoughts on minimalism in design and code' description: 'Thoughts on minimalism in design and code'
pubDate: '2025-06-05' pubDate: '2025-06-05'
author: 'Wheatley'
--- ---
Minimalism isn't just about having less, it's about making room for what matters. Minimalism isn't just about having less, it's about making room for what matters.

10
src/data/authors.yaml Normal file
View file

@ -0,0 +1,10 @@
Wheatley: # the key name (id) of the author, which is used in the front matter
name: "Wheatley" # the display name of the author
email: "hello@example.org" # the email address of the author
social: # the social media accounts of the author, if any (there is no reference for this yet except for the twitter handle)
twitter: "@wheatley"
fediverse: "@"
Glados: # you can have more if you like
name: "Glados"
email: "contact@example.org"

View file

@ -12,6 +12,7 @@ import Logo from '../assets/mercury.svg'
interface Props { interface Props {
title: string; title: string;
description: string; description: string;
author?: string;
path?: string; path?: string;
ogImage?: string; ogImage?: string;
} }
@ -29,7 +30,7 @@ const navBarItems = siteConfig.navBarItems
const customFooter = siteConfig.customFooter const customFooter = siteConfig.customFooter
const nekoType = siteConfig.neko?.type const nekoType = siteConfig.neko?.type
const { title = pageTitle, description = siteConfig.description, ogImage = "" } = Astro.props; const { title = pageTitle, author = siteConfig.defaultAuthor.name,description = siteConfig.description, ogImage = "" } = Astro.props;
--- ---
<!doctype html> <!doctype html>
@ -39,7 +40,7 @@ const { title = pageTitle, description = siteConfig.description, ogImage = "" }
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} /> <meta name="generator" content={Astro.generator} />
<Meta title={pageTitle} description={description} ogImage={ogImage} /> <Meta title={pageTitle} author={author} description={description} ogImage={ogImage} />
<title>{pageTitle}</title> <title>{pageTitle}</title>
</head> </head>
<body> <body>
@ -74,7 +75,7 @@ const { title = pageTitle, description = siteConfig.description, ogImage = "" }
<p>Powered by <a href="https://git.gb0.dev/gb/mercury" target="_blank"><Logo width={16} height={16} /> mercury</a></p> <p>Powered by <a href="https://git.gb0.dev/gb/mercury" target="_blank"><Logo width={16} height={16} /> mercury</a></p>
</div> </div>
</footer> </footer>
{ !noscript || siteConfig.neko.enabled && { (siteConfig.neko.enabled && !noscript) &&
<> <>
<script is:inline define:vars={{ nekoType }}> <script is:inline define:vars={{ nekoType }}>
window.NekoType = nekoType; window.NekoType = nekoType;

View file

@ -1,6 +1,6 @@
--- ---
import Layout from '../../layouts/Layout.astro'; import Layout from '../../layouts/Layout.astro';
import { getCollection } from 'astro:content'; import { getCollection, getEntry } from 'astro:content';
import Comments from "../../components/Comments.astro"; import Comments from "../../components/Comments.astro";
import {getImage} from "astro:assets"; import {getImage} from "astro:assets";
import { unified } from "unified"; import { unified } from "unified";
@ -21,6 +21,11 @@ const { entry } = Astro.props;
const { Content } = await entry.render(); const { Content } = await entry.render();
const noscript = siteConfig.noClientJavaScript const noscript = siteConfig.noClientJavaScript
const slug = Astro.params.slug; const slug = Astro.params.slug;
const authorId = entry.data.author || siteConfig.defaultAuthor.id;
// Get author data
const authorData = await getEntry('authors', authorId);
const authorInfo = authorData ? authorData.data : siteConfig.defaultAuthor;
// get featured image and use it as og:image // get featured image and use it as og:image
// use the custom cover image if it exists, otherwise use the featured image file in the same directory // use the custom cover image if it exists, otherwise use the featured image file in the same directory
@ -39,6 +44,7 @@ const cover = customFeaturedImage || matchedImage_src?.src || `/post/${slug}/fea
title={entry.data.title} title={entry.data.title}
description={entry.data.description} description={entry.data.description}
ogImage={cover} ogImage={cover}
author={authorInfo.name}
> >
<h1 class="title">{entry.data.title}</h1> <h1 class="title">{entry.data.title}</h1>
<span class="date">{new Date(entry.data.pubDate).toISOString().split('T')[0]}</span> <span class="date">{new Date(entry.data.pubDate).toISOString().split('T')[0]}</span>
@ -47,7 +53,7 @@ const cover = customFeaturedImage || matchedImage_src?.src || `/post/${slug}/fea
</div> </div>
<div style="margin-top: 2rem; border-top: 1px solid var(--border-color); padding-top: 1rem;"> <div style="margin-top: 2rem; border-top: 1px solid var(--border-color); padding-top: 1rem;">
<ReplyViaEmail title={entry.data.title} /> <ReplyViaEmail title={entry.data.title} email={authorInfo.email} />
<br> <br>
<a href="/blog">&larr; Back to posts</a> <a href="/blog">&larr; Back to posts</a>
{noscript && <h2>Comments</h2> <Comments path={`post/${slug}`} />} {noscript && <h2>Comments</h2> <Comments path={`post/${slug}`} />}