Compare commits
2 commits
e605e4ed42
...
458dc2dafe
Author | SHA1 | Date | |
---|---|---|---|
|
458dc2dafe | ||
|
af55568839 |
9 changed files with 55 additions and 9 deletions
|
@ -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}".`}>↩ Reply via Email</a>
|
<a href={`mailto:${email}?subject=RE:${title}&body=Hi,\n\nI would like to reply to your post "${title}".`}>↩ Reply via Email</a>
|
|
@ -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} />
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
|
@ -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(),
|
||||||
});
|
});
|
|
@ -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
10
src/data/authors.yaml
Normal 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"
|
|
@ -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;
|
||||||
|
|
|
@ -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">← Back to posts</a>
|
<a href="/blog">← Back to posts</a>
|
||||||
{noscript && <h2>Comments</h2> <Comments path={`post/${slug}`} />}
|
{noscript && <h2>Comments</h2> <Comments path={`post/${slug}`} />}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue