Compare commits

..

No commits in common. "98d23e7c94e8e2708f2044a5d2345288adcc5d74" and "64d01a9b45cc5861fbb77d6976da559b300e17bc" have entirely different histories.

13 changed files with 45 additions and 490 deletions

View file

@ -1,93 +0,0 @@
/* https://github.com/oom-components/mastodon-comments, under MIT License */
oom-comments {
display: block;
padding: 2em;
}
oom-comments ul {
list-style: none;
margin: 0;
padding: 0;
}
oom-comments li {
margin: 32px 0;
}
oom-comments article {
max-width: 600px;
}
oom-comments ul ul {
margin-left: 64px;
}
oom-comments .comment-avatar {
width: 50px;
height: 50px;
border-radius: 6px;
float: left;
margin-right: 14px;
box-shadow: 0 0 1px #0009;
}
oom-comments .comment-user {
color: currentColor;
text-decoration: none;
display: block;
position: relative;
}
oom-comments .comment-author {
position: absolute;
left: 35px;
top: 35px;
background: white;
border-radius: 50%;
width: 20px;
height: 20px;
color: gray;
}
oom-comments .comment-user:hover .comment-username {
text-decoration: underline;
}
oom-comments .comment-username {
margin-right: 0.5em;
}
oom-comments .comment-useraddress {
color: gray;
font-size: small;
font-style: normal;
}
oom-comments .comment-time {
font-size: small;
display: flex;
align-items: center;
column-gap: 0.4em;
}
oom-comments .comment-time svg {
width: 1em;
height: 1em;
fill: gray;
}
oom-comments .comment-address {
color: currentColor;
text-decoration: none;
display: block;
margin-top: 0.25em;
}
oom-comments .comment-address:hover {
text-decoration: underline;
}
oom-comments .comment-body {
margin-top: 0.5em;
margin-left: 64px;
line-height: 1.5;
}
oom-comments .comment-body p {
margin: 0.5em 0;
}
oom-comments .comment-counts {
display: flex;
column-gap: 1em;
font-size: small;
}
oom-comments .comment-counts > span {
display: flex;
align-items: center;
column-gap: 0.3em;
color: gray;
}

View file

@ -1,312 +0,0 @@
/* https://github.com/oom-components/mastodon-comments, under MIT License */
// © https://phosphoricons.com/
export const icons = {
reblog:
`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 256 256" fill="none" stroke="currentColor" stroke-width="24"><polyline points="200 88 224 64 200 40"/><path d="M32,128A64,64,0,0,1,96,64H224"/><polyline points="56 168 32 192 56 216"/><path d="M224,128a64,64,0,0,1-64,64H32"/></svg>`,
favourite:
`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 256 256" fill="currentColor"><path d="M240,94c0,70-103.79,126.66-108.21,129a8,8,0,0,1-7.58,0C119.79,220.66,16,164,16,94A62.07,62.07,0,0,1,78,32c20.65,0,38.73,8.88,50,23.89C139.27,40.88,157.35,32,178,32A62.07,62.07,0,0,1,240,94Z"/></svg>`,
author:
`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 256 256" fill="currentColor" class="comment-author"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm45.66,85.66-56,56a8,8,0,0,1-11.32,0l-24-24a8,8,0,0,1,11.32-11.32L112,148.69l50.34-50.35a8,8,0,0,1,11.32,11.32Z"></path></svg>`,
// @ https://simpleicons.org/
mastodon:
`<svg role="img" width="16" height="16" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Mastodon</title><path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/></svg>`,
pleroma:
`<svg role="img" width="16" height="16" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Pleroma</title><path d="M6.36 0A1.868 1.868 0 004.49 1.868V24h5.964V0zm7.113 0v12h4.168a1.868 1.868 0 001.868-1.868V0zm0 18.036V24h4.168a1.868 1.868 0 001.868-1.868v-4.096Z"/></svg>`,
bluesky:
`<svg role="img" width="16" height="16" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Bluesky</title><path d="M12 10.8c-1.087-2.114-4.046-6.053-6.798-7.995C2.566.944 1.561 1.266.902 1.565.139 1.908 0 3.08 0 3.768c0 .69.378 5.65.624 6.479.815 2.736 3.713 3.66 6.383 3.364.136-.02.275-.039.415-.056-.138.022-.276.04-.415.056-3.912.58-7.387 2.005-2.83 7.078 5.013 5.19 6.87-1.113 7.823-4.308.953 3.195 2.05 9.271 7.733 4.308 4.267-4.308 1.172-6.498-2.74-7.078a8.741 8.741 0 0 1-.415-.056c.14.017.279.036.415.056 2.67.297 5.568-.628 6.383-3.364.246-.828.624-5.79.624-6.478 0-.69-.139-1.861-.902-2.206-.659-.298-1.664-.62-4.3 1.24C16.046 4.748 13.087 8.687 12 10.8Z"/></svg>`,
};
export default class SocialComments extends HTMLElement {
comments = {};
async connectedCallback() {
const lang = this.closest("[lang]")?.lang || navigator.language || "en";
this.dateTimeFormatter = new Intl.DateTimeFormat(lang, {
dateStyle: "medium",
timeStyle: "short",
});
const mastodon = this.getAttribute("mastodon") || this.getAttribute("src");
const bluesky = this.getAttribute("bluesky");
await Promise.all([
mastodon && this.#fetchMastodon(new URL(mastodon)),
bluesky && this.#fetchBluesky(new URL(bluesky)),
]);
this.refresh();
}
refresh() {
const comments = [
...this.comments.mastodon || [],
...this.comments.bluesky || [],
].sort(
(a, b) => new Date(a.createdAt) - new Date(b.createdAt),
);
if (comments.length) {
this.innerHTML = "";
this.render(this, comments);
}
}
async #fetchBluesky(url) {
const { pathname } = url;
const [, handle, rkey] = pathname.match(
/\/profile\/([\w\.]+)\/post\/(\w+)/,
);
if (!handle || !rkey) {
return;
}
const options = {
ttl: Number(this.getAttribute("cache") || 0),
};
const didData = await fetchJSON(
`https://public.api.bsky.app/xrpc/com.atproto.identity.resolveHandle?handle=${handle}`,
options,
);
const uri = `at://${didData.did}/app.bsky.feed.post/${rkey}`;
this.comments.bluesky = dataFromBluesky(
await fetchJSON(
`https://public.api.bsky.app/xrpc/app.bsky.feed.getPostThread?uri=${uri}`,
options,
),
);
}
async #fetchMastodon(url) {
const { origin, pathname } = url;
let id;
const source = pathname.includes("/notice/") ? "pleroma" : "mastodon";
if (source === "pleroma") {
[, id] = pathname.match(/^\/notice\/([^\/?#]+)/);
} else {
[, id] = pathname.match(/\/(\d+)$/);
}
if (!id) {
return;
}
const token = this.getAttribute("token");
const options = {
ttl: Number(this.getAttribute("cache") || 0),
};
if (token) {
options.headers = {
Authorization: `Bearer ${token}`,
};
}
const user = url.pathname.split("/")[1];
const author = `${user}@${url.hostname}`;
const comments = dataFromMastodon(
await fetchJSON(
new URL(`${origin}/api/v1/statuses/${id}/context`),
options,
),
author,
source,
);
this.comments.mastodon = comments.filter((comment) =>
comment.parent === id
);
}
render(container, replies) {
const ul = document.createElement("ul");
for (const reply of replies) {
const comment = document.createElement("li");
comment.innerHTML = this.renderComment(reply);
if (reply.replies.length) {
this.render(comment, reply.replies);
}
ul.appendChild(comment);
}
container.appendChild(ul);
}
renderComment(comment) {
return `
<article class="comment" id="comment-${comment.id}">
<footer class="comment-footer">
<a href="${comment.author.url}" class="comment-user">
<img class="comment-avatar" src="${comment.author.avatar}" alt="${comment.author.alt}'s avatar" width="200" height="200">
${comment.isMine ? icons.author : ""}
<strong class="comment-username">
${comment.author.name}
</strong>
<em class="comment-useraddress">${comment.author.handler}</em>
</a>
<a href="${comment.url}" class="comment-address">
<time class="comment-time" title="${comment.createdAt.toISOString()}">
${this.dateTimeFormatter.format(comment.createdAt)}
${icons[comment.source]}
</time>
</a>
</footer>
<div class="comment-body">
${comment.content}
<p class="comment-counts">
${
comment.boosts ? `<span>${icons.reblog} ${comment.boosts}</span>` : ""
}
${
comment.likes ? `<span>${icons.favourite} ${comment.likes}</span>` : ""
}
</p>
</div>
</article>
`;
}
}
function formatEmojis(html, emojis) {
emojis.forEach(({ shortcode, static_url, url }) => {
html = html.replace(
`:${shortcode}:`,
`<picture>
<source srcset="${url}" media="(prefers-reduced-motion: no-preference)">
<img src="${static_url}" alt=":${shortcode}:" title=":${shortcode}:" width="16" height="16">
</picture>`,
);
});
return html;
}
async function fetchJSON(url, options = {}) {
const headers = new Headers();
if (options.headers) {
for (const [key, value] of Object.entries(options.headers)) {
headers.set(key, value);
}
}
if (typeof caches === "undefined") {
return await (await fetch(url), { headers }).json();
}
const cache = await caches.open("mastodon-comments");
let cached = await cache.match(url);
if (cached && options.ttl) {
const cacheTime = new Date(cached.headers.get("x-cached-at"));
const diff = Date.now() - cacheTime.getTime();
if (diff <= options.ttl * 1000) {
return await cached.json();
}
}
try {
const response = await fetch(url, { headers });
const body = await response.json();
cached = new Response(JSON.stringify(body));
cached.headers.set("x-cached-at", new Date());
cached.headers.set("content-type", "application/json; charset=utf-8");
await cache.put(url, cached);
return body;
} catch {
if (cached) {
return await cached.json();
}
}
}
function dataFromMastodon(data, author, source) {
const comments = new Map();
// Transform data to a more usable format
for (const comment of data.descendants) {
if (comment.visibility !== "public") {
continue;
}
const { account } = comment;
const handler = `@${account.username}@${new URL(account.url).hostname}`;
comments.set(comment.id, {
id: comment.id,
isMine: author === handler,
source,
url: comment.url,
parent: comment.in_reply_to_id,
createdAt: new Date(comment.created_at),
content: formatEmojis(comment.content, comment.emojis),
author: {
name: formatEmojis(account.display_name, account.emojis),
handler,
url: account.url,
avatar: account.avatar_static,
alt: account.display_name,
},
boosts: comment.reblogs_count,
likes: comment.favourites_count,
replies: [],
});
}
// Group comments by parent
for (const comment of comments.values()) {
if (comment.parent && comments.has(comment.parent)) {
comments.get(comment.parent).replies.push(comment);
}
}
return Array.from(comments.values());
}
function dataFromBluesky(data) {
const { thread } = data;
return blueskyComments(
thread.post.author.did,
thread.post.cid,
thread.replies,
);
}
function blueskyComments(author, parent, comments) {
return comments.map((reply) => {
const { post, replies } = reply;
const rkey = post.uri.split("/").pop();
return {
id: post.cid,
isMine: post.author.did === author,
source: "bluesky",
url: `https://bsky.app/profile/${post.author.handle}/post/${rkey}`,
parent,
createdAt: new Date(post.record.createdAt),
content: post.record.text,
author: {
name: post.author.displayName,
handler: post.author.handle,
url: `https://bsky.app/profile/${post.author.handle}`,
avatar: post.author.avatar,
alt: post.author.displayName,
},
boosts: post.repostCount,
likes: post.likeCount,
replies: blueskyComments(author, post.cid, replies || []),
};
});
}

View file

@ -4,18 +4,11 @@ import FediverseComments from "./helper/comments/Fediverse.astro";
import HatsuComments from "./helper/comments/Hatsu.astro"; import HatsuComments from "./helper/comments/Hatsu.astro";
import Artalk from "./helper/comments/Artalk.astro"; import Artalk from "./helper/comments/Artalk.astro";
import Giscus from "./helper/comments/Giscus.astro"; import Giscus from "./helper/comments/Giscus.astro";
import OomComments from "./helper/comments/OOM.astro";
const method = siteConfig.comments.type const method = siteConfig.comments.type
const FediverseConfig = siteConfig.comments.fediverse const FediverseConfig = siteConfig.comments.fediverse
interface Props {
mastodonLink?: string;
bskyLink?: string;
}
const { mastodonLink, bskyLink } = Astro.props;
--- ---
{method === 'artalk' && <Artalk />} {method === 'artalk' && <Artalk />}
{method === 'giscus' && <Giscus />} {method === 'giscus' && <Giscus />}
@ -23,4 +16,3 @@ const { mastodonLink, bskyLink } = Astro.props;
{(method === 'fediverse' && !FediverseConfig.renderOnServer ) && <FediverseComments /> } {(method === 'fediverse' && !FediverseConfig.renderOnServer ) && <FediverseComments /> }
{(method === 'fediverse' && FediverseConfig.renderOnServer ) && <FediverseComments server:defer><p>Loading comments...</p></FediverseComments> } {(method === 'fediverse' && FediverseConfig.renderOnServer ) && <FediverseComments server:defer><p>Loading comments...</p></FediverseComments> }
{method === 'hatsu' && <HatsuComments /> } {method === 'hatsu' && <HatsuComments /> }
{method === 'oom' && <OomComments mastodonLink={mastodonLink} bskyLink={bskyLink} />}

View file

@ -5,5 +5,5 @@ const navBarItems = siteConfig.navBarItems
<nav class="nav"> <nav class="nav">
<a href="/" class="home">~</a> <a href="/" class="home">~</a>
<a href="/blog">Blog</a> <a href="/blog">Blog</a>
{navBarItems.map((item) => <a href={item.link} target={item.openInNewTab !== false ? "_blank" : undefined}>{item.text}</a>)} {navBarItems.map((item) => <a href={item.link}>{item.text}</a>)}
</nav> </nav>

View file

@ -73,9 +73,6 @@ const toc = buildHierarchy(filteredHeadings);
<style> <style>
details { details {
margin: 0.5rem auto; margin: 0.5rem auto;
cursor: pointer;
padding: 0.5rem;
border: var(--accent-color) 0.1rem solid;
} }
.toc-list { .toc-list {
list-style-type: none; list-style-type: none;

View file

@ -50,19 +50,25 @@
<script> <script>
// Constants for theme options // Constants for theme options
const THEME_AUTO = 'auto'; const THEME_AUTO = 'auto';
const THEME_DARK = 'dark';
const THEME_LIGHT = 'light';
// DOM elements // DOM elements
const themeDropdown = document.getElementById('theme-dropdown');
const menuBody = document.getElementById('menu-body');
const dropdownItems = document.querySelectorAll('.dropdown-item');
const root = document.documentElement; const root = document.documentElement;
// Get system preference // Get system preference
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const systemTheme = prefersDark ? THEME_DARK : THEME_LIGHT;
// Get saved theme from localStorage or default to auto // Get saved theme from localStorage or default to auto
const savedThemeMode = localStorage.getItem('themeMode') || THEME_AUTO; const savedThemeMode = localStorage.getItem('themeMode') || THEME_AUTO;
const savedTheme = localStorage.getItem('theme');
// Function to update active state in dropdown menu // Function to update active state in dropdown menu
function updateActiveState(activeTheme) { function updateActiveState(activeTheme) {
const dropdownItems = document.querySelectorAll('.dropdown-item');
dropdownItems.forEach(item => { dropdownItems.forEach(item => {
if (item.getAttribute('data-theme') === activeTheme) { if (item.getAttribute('data-theme') === activeTheme) {
item.classList.add('active'); item.classList.add('active');
@ -92,32 +98,22 @@
} }
} }
function addHandlers(){
const dropdownItems = document.querySelectorAll('.dropdown-item');
// Add click handlers to dropdown items
dropdownItems.forEach(item => {
item.addEventListener('click', () => {
const selectedTheme = item.getAttribute('data-theme');
applyTheme(selectedTheme);
});
});
// Listen for system preference changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
if (savedThemeMode === THEME_AUTO) {
// Only update if we're in auto mode
root.removeAttribute('data-theme');
}
});
}
// Initialize theme // Initialize theme
applyTheme(savedThemeMode); applyTheme(savedThemeMode);
addHandlers();
// Reapply theme on page switch // Add click handlers to dropdown items
document.addEventListener('astro:page-load', () => { dropdownItems.forEach(item => {
applyTheme(savedThemeMode || THEME_AUTO); item.addEventListener('click', () => {
addHandlers(); const selectedTheme = item.getAttribute('data-theme');
applyTheme(selectedTheme);
}); });
});
// Listen for system preference changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
if (savedThemeMode === THEME_AUTO) {
// Only update if we're in auto mode
root.removeAttribute('data-theme');
}
});
</script> </script>

View file

@ -1,17 +1,18 @@
--- ---
import {Image} from "astro:assets"; import {Image} from "astro:assets";
import {getEntry} from "astro:content";
import {siteConfig} from "../../../config"; import {siteConfig} from "../../../config";
const { data } = Astro.props; const { data } = Astro.props;
// Get author data // Get author data
const authorAvatar = data.mcplayerid ? `/images/avatars/${data.mcplayerid}.png` : null; const authorAvatar = data?.data.mcplayerid ? `/images/avatars/${data.id}.png` : null;
const authorName = data.name ? data.name : null; const authorName = data ? data.data.name : null;
--- ---
{(siteConfig.displayAvatar && data) && {(siteConfig.displayAvatar && data) &&
<> <>
{authorAvatar && <Image src={authorAvatar} alt={`avatar of ${authorName}`} width=16 height=16 />} {authorAvatar && <Image src={authorAvatar} alt={`avatar of ${authorName}`} width=16 height=16 />}
<span>{authorName}</span> | <span>{authorName} @ </span>
</> </>
} }
<style> <style>

View file

@ -1,15 +0,0 @@
---
import "../../../assets/css/oom-styles.css";
const { mastodonLink, bskyLink } = Astro.props;
---
<oom-comments
mastodon={mastodonLink}
bluesky={bskyLink}
cache="600" >
No comments yet
</oom-comments>
<script>
import Comments from "../../../assets/js/oom-comments.js";
//Register the custom element with your desired name
customElements.define("oom-comments", Comments);
</script>

View file

@ -20,9 +20,9 @@ export const siteConfig = {
navBarItems: [ navBarItems: [
// additional items in the navbar // additional items in the navbar
// the order of the items will be the same as the order in the array // the order of the items will be the same as the order in the array
// format is { text: string, link: string, openInNewTab?: boolean (default: true) } // format is { text: string, link: string }
{ text: "RSS", link: "/rss.xml", openInNewTab: true }, { text: "RSS", link: "/rss.xml" },
{ text: "GitHub", link: "https://github.com/GrassBlock1/mercury", openInNewTab: false }, { text: "GitHub", link: "https://github.com/GrassBlock1/mercury" },
], ],
// search // search
// This only works when noClientJavaScript is enabled // This only works when noClientJavaScript is enabled
@ -44,7 +44,7 @@ export const siteConfig = {
contentPassword: 'p1easeChangeMe!', contentPassword: 'p1easeChangeMe!',
// comments // comments
comments: { comments: {
type: 'artalk', // 'artalk','giscus','fediverse','email','hatsu','oom' type: 'artalk', // 'artalk','giscus','fediverse','email','hatsu'
artalk: { artalk: {
instanceDomain: '', // the domain of your artalk instance instanceDomain: '', // the domain of your artalk instance
}, },

View file

@ -1,14 +1,12 @@
import { z, reference } from 'astro:content'; import { z, reference } from 'astro:content';
// @ts-ignore
export const posts = ({ image }) => z.object({ export const posts = ({ image }) => z.object({
title: z.string(), title: z.string(),
description: z.string(), description: z.string(),
summary: z.string().optional(),
pubDate: z.coerce.date(), pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(), updatedDate: z.coerce.date().optional(),
categories: z.array(z.string()).default(['uncategorized']), categories: z.array(z.string()).default(['uncategorized']),
tags: z.array(z.string()).optional(), tags: z.array(z.string()).optional(),
cover: image().optional(), cover: image().optional(),
author: z.union([z.array(reference('authors')), reference('authors')]).optional(), author: reference('authors').optional(),
}); });

View file

@ -1,7 +1,6 @@
--- ---
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'
summary: 'Exploring the principles of minimalism and its application in design and coding practices.'
pubDate: '2025-06-05' pubDate: '2025-06-05'
author: 'Wheatley' author: 'Wheatley'
tags: tags:

View file

@ -2,9 +2,6 @@
title: 'My Terminal Setup' title: 'My Terminal Setup'
description: 'A walkthrough of my current terminal configuration' description: 'A walkthrough of my current terminal configuration'
pubDate: '2025-06-08' pubDate: '2025-06-08'
author:
- 'Glados'
- 'Wheatley'
--- ---
![cover from mohammad-rahmani-oXlXu2qukGE-unsplash](./demo.png) ![cover from mohammad-rahmani-oXlXu2qukGE-unsplash](./demo.png)
Here's my current terminal setup: Here's my current terminal setup:

View file

@ -19,15 +19,16 @@ export async function getStaticPaths() {
} }
const { entry } = Astro.props; const { entry } = Astro.props;
const { Content, headings } = await entry.render(); const { Content } = await entry.render();
const headings = await entry.render().then(rendered => rendered.headings);
const noscript = siteConfig.noClientJavaScript const noscript = siteConfig.noClientJavaScript
const slug = Astro.params.slug; const slug = Astro.params.slug;
const author = Array.isArray(entry.data.author) ? entry.data.author : (entry.data.author !== undefined ? [entry.data.author] : [{collection: 'authors', id: siteConfig.defaultAuthor.id}]); const author = entry.data.author || {collection: 'authors', id: siteConfig.defaultAuthor.id};
// Get author data // Get author data
const authorData = await Promise.all((author).map((singleAuthor) => getEntry(singleAuthor).then(authorEntry => authorEntry.data))) const authorData = await getEntry(author);
const authorInfo = authorData.includes(undefined) ? [{data: siteConfig.defaultAuthor}] : authorData; 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
@ -36,7 +37,7 @@ const customFeaturedImage = entry.data.cover?.src
const matchedImage = Object.keys(featuredImages).find(path => path.includes(slug)); const matchedImage = Object.keys(featuredImages).find(path => path.includes(slug));
let matchedImage_src; let matchedImage_src;
if (matchedImage && !customFeaturedImage) { if (matchedImage && !customFeaturedImage) {
matchedImage_src = await getImage({src: featuredImages[matchedImage] as ImageMetadata, format: 'webp'}) || null; matchedImage_src = await getImage({src: featuredImages[matchedImage], format: 'webp'}) || null;
} }
const firstImageURL = await ExtractFirstImage(Content) const firstImageURL = await ExtractFirstImage(Content)
@ -47,19 +48,18 @@ const cover = customFeaturedImage || matchedImage_src?.src || firstImageURL || `
title={entry.data.title} title={entry.data.title}
description={entry.data.description} description={entry.data.description}
ogImage={cover} ogImage={cover}
author={authorInfo.map((a: any) => a.name).join(', ')} author={authorInfo.name}
> >
<h1 class="title">{entry.data.title}</h1> <h1 class="title">{entry.data.title}</h1>
{authorInfo.map((a: any) => <AuthorInfo data={a} />)} <AuthorInfo data={authorData} />
<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>
{headings.length !== 0 && <TableOfContents headings={headings} />} {headings.length !== 0 && <TableOfContents headings={headings} />}
{entry.data.summary && <p class="summary">{entry.data.summary}</p> }
<div class="content"> <div class="content">
<Content /> <Content />
</div> </div>
<div class="extra-post" style="margin-top: 2rem; border-top: 1px solid var(--border-color); padding-top: 1rem;"> <div class="extra-post" style="margin-top: 2rem; border-top: 1px solid var(--border-color); padding-top: 1rem;">
<ReplyViaEmail title={entry.data.title} email={authorInfo[0].email} /> <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 />} {!noscript && <h2>Comments</h2> <Comments />}
@ -69,11 +69,6 @@ const cover = customFeaturedImage || matchedImage_src?.src || firstImageURL || `
</script> </script>
} }
</div> </div>
</Layout> </Layout>
<style>
p.summary {
font-style: italic;
color: var(--secondary-text-color);
margin: 1rem;
}
</style>