diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..eb2feaf
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,3 @@
+dist/**/*
+tmp/**/*
+public/js/fslightbox.js
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..5e64263
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,12 @@
+{
+ "trailingComma": "es5",
+ "singleQuote": true,
+ "overrides": [
+ {
+ "files": ["*.ts", "*.astro"],
+ "options": {
+ "semi": false
+ }
+ }
+ ]
+}
diff --git a/astro.config.ts b/astro.config.ts
index ff6b8e0..711a9e4 100644
--- a/astro.config.ts
+++ b/astro.config.ts
@@ -19,7 +19,8 @@ import { pluginLineNumbers } from '@expressive-code/plugin-line-numbers'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
- site: 'https://astro-erudite.vercel.app',
+ site: 'https://nvme0n1p.dev',
+ output: 'server',
integrations: [
expressiveCode({
themes: ['github-light', 'github-dark'],
diff --git a/src/components/BlogCard.astro b/src/components/BlogCard.astro
index 1a74025..394fe0f 100644
--- a/src/components/BlogCard.astro
+++ b/src/components/BlogCard.astro
@@ -73,8 +73,6 @@ const subpostCount = !isSubpost(entry.id) ? await getSubpostCount(entry.id) : 0
)
}
{formattedDate}
-
- {readTime}
{
subpostCount > 0 && (
<>
diff --git a/src/components/SubpostsHeader.astro b/src/components/SubpostsHeader.astro
index 0f1c597..dc933cf 100644
--- a/src/components/SubpostsHeader.astro
+++ b/src/components/SubpostsHeader.astro
@@ -5,7 +5,6 @@ import {
getParentId,
getParentPost,
getPostById,
- getPostReadingTime,
getSubpostsForParent,
isSubpost,
} from '@/lib/data-utils'
@@ -23,17 +22,9 @@ const parentPost = isCurrentSubpost ? await getParentPost(currentPostId) : null
const activePost = parentPost || currentPost
const isActivePost = activePost?.id === currentPostId
-const activePostReadingTime = activePost
- ? await getPostReadingTime(activePost.id)
- : null
-const activePostCombinedReadingTime =
- activePost && subposts.length > 0
- ? await getCombinedReadingTime(activePost.id)
- : null
const subpostsWithReadingTime = await Promise.all(
subposts.map(async (subpost) => ({
...subpost,
- readingTime: await getPostReadingTime(subpost.id),
})),
)
@@ -97,7 +88,7 @@ const currentSubpostDetails = isCurrentSubpost
{activePost.data.title}
- {activePostReadingTime && (
+ {/* {activePostReadingTime && (
{activePostReadingTime}
{activePostCombinedReadingTime &&
@@ -109,7 +100,7 @@ const currentSubpostDetails = isCurrentSubpost
)}
- )}
+ )} */}
) : (
@@ -126,19 +117,6 @@ const currentSubpostDetails = isCurrentSubpost
{activePost.data.title}
- {activePostReadingTime && (
-
- {activePostReadingTime}
- {activePostCombinedReadingTime &&
- activePostCombinedReadingTime !==
- activePostReadingTime && (
-
- {' '}
- ({activePostCombinedReadingTime} total)
-
- )}
-
- )}
)}
@@ -157,9 +135,6 @@ const currentSubpostDetails = isCurrentSubpost
/>
{subpost.data.title}
-
- {subpost.readingTime}
-
) : (
@@ -174,9 +149,6 @@ const currentSubpostDetails = isCurrentSubpost
/>
{subpost.data.title}
-
- {subpost.readingTime}
-
),
diff --git a/src/components/SubpostsSidebar.astro b/src/components/SubpostsSidebar.astro
index 3428583..fae980d 100644
--- a/src/components/SubpostsSidebar.astro
+++ b/src/components/SubpostsSidebar.astro
@@ -6,7 +6,6 @@ import {
getParentId,
getParentPost,
getPostById,
- getPostReadingTime,
getSubpostsForParent,
isSubpost,
} from '@/lib/data-utils'
@@ -24,17 +23,9 @@ const parentPost = isCurrentSubpost ? await getParentPost(currentPostId) : null
const activePost = parentPost || currentPost
const isActivePost = activePost?.id === currentPostId
-const activePostReadingTime = activePost
- ? await getPostReadingTime(activePost.id)
- : null
-const activePostCombinedReadingTime =
- activePost && subposts.length > 0
- ? await getCombinedReadingTime(activePost.id)
- : null
const subpostsWithReadingTime = await Promise.all(
subposts.map(async (subpost) => ({
...subpost,
- readingTime: await getPostReadingTime(subpost.id),
})),
)
---
@@ -64,19 +55,6 @@ const subpostsWithReadingTime = await Promise.all(
{activePost.data.title}
- {activePostReadingTime && (
-
- {activePostReadingTime}
- {activePostCombinedReadingTime &&
- activePostCombinedReadingTime !==
- activePostReadingTime && (
-
- {' '}
- ({activePostCombinedReadingTime} total)
-
- )}
-
- )}
) : (
@@ -93,19 +71,6 @@ const subpostsWithReadingTime = await Promise.all(
{activePost.data.title}
- {activePostReadingTime && (
-
- {activePostReadingTime}
- {activePostCombinedReadingTime &&
- activePostCombinedReadingTime !==
- activePostReadingTime && (
-
- {' '}
- ({activePostCombinedReadingTime} total)
-
- )}
-
- )}
)}
@@ -128,9 +93,6 @@ const subpostsWithReadingTime = await Promise.all(
{subpost.data.title}
-
- {subpost.readingTime}
-
) : (
@@ -147,9 +109,6 @@ const subpostsWithReadingTime = await Promise.all(
{subpost.data.title}
-
- {subpost.readingTime}
-
),
diff --git a/src/consts.ts b/src/consts.ts
index 689b337..3989508 100644
--- a/src/consts.ts
+++ b/src/consts.ts
@@ -18,6 +18,10 @@ export const NAV_LINKS: SocialLink[] = [
href: '/blog',
label: 'blog',
},
+ {
+ href: '/tags',
+ label: 'tags',
+ },
{
href: '/about',
label: 'about',
diff --git a/src/lib/data-utils.ts b/src/lib/data-utils.ts
index 5b8b784..301e02a 100644
--- a/src/lib/data-utils.ts
+++ b/src/lib/data-utils.ts
@@ -9,6 +9,17 @@ import {
} from './blog-helpers'
const DEFAULT_AUTHOR_ID = 'libr'
+type ContentCollection = 'blog' | 'authors' | 'projects'
+
+async function getCollectionSafe(
+ name: T,
+): Promise[]> {
+ try {
+ return await getCollection(name)
+ } catch {
+ return []
+ }
+}
type NotionPost = {
id: string
@@ -56,14 +67,14 @@ export interface LinkEntry {
}
export async function getAllAuthors(): Promise[]> {
- return await getCollection('authors')
+ return getCollectionSafe('authors')
}
export const POSTS_API_URL = 'https://notion-api.nvme0n1p.dev/v2/posts'
export async function getAllPosts(): Promise[]> {
const fallback = async () => {
- const posts = await getCollection('blog')
+ const posts = await getCollectionSafe('blog')
return posts
.filter((post) => !post.data.draft && !isSubpost(post.id))
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf())
@@ -91,6 +102,7 @@ export async function getAllPosts(): Promise[]> {
const date = new Date(dateString)
const id = post.slug || post.id
const banner = (post as any).banner || null
+ const image = banner
return {
id,
@@ -103,6 +115,7 @@ export async function getAllPosts(): Promise[]> {
draft: !(post.Published ?? true),
authors: [DEFAULT_AUTHOR_ID],
banner,
+ image,
},
body: '',
}
@@ -125,7 +138,7 @@ export async function getAllPostsAndSubposts(): Promise<
}
export async function getAllProjects(): Promise[]> {
- const projects = await getCollection('projects')
+ const projects = await getCollectionSafe('projects')
return projects.sort((a, b) => {
const dateA = a.data.startDate?.getTime() || 0
const dateB = b.data.startDate?.getTime() || 0
@@ -155,7 +168,7 @@ export async function getAdjacentPosts(currentId: string): Promise<{
const allPosts = await getAllPosts()
const parent = allPosts.find((post) => post.id === parentId) || null
- const posts = await getCollection('blog')
+ const posts = await getCollectionSafe('blog')
const subposts = posts
.filter(
(post) =>
@@ -242,7 +255,7 @@ export function getParentId(subpostId: string): string {
export async function getSubpostsForParent(
parentId: string,
): Promise[]> {
- const posts = await getCollection('blog')
+ const posts = await getCollectionSafe('blog')
return posts
.filter(
(post) =>
@@ -305,6 +318,12 @@ export async function fetchRemotePostContent(
if (!res.ok) throw new Error(`Failed to fetch post content: ${res.status}`)
const data = (await res.json()) as Partial
if (!data.post || !data.blockMap) return null
+
+ const post = data.post as any
+ if (post?.banner && !post?.image) {
+ post.image = post.banner
+ }
+
return data as RemotePostPayload
} catch (error) {
console.error(`fetchRemotePostContent error for slug "${slug}":`, error)
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index 8e5a7a0..052565d 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -1,5 +1,6 @@
import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
+import type { ImageMetadata } from 'astro:assets'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
@@ -35,3 +36,19 @@ export function getHeadingMargin(depth: number): string {
}
return margins[depth] || ''
}
+
+export type ImageSource = string | ImageMetadata
+
+export function normalizeImageSource(value: unknown): ImageSource | null {
+ if (!value) return null
+ if (typeof value === 'string') return value
+ if (
+ typeof value === 'object' &&
+ value !== null &&
+ 'src' in value &&
+ typeof (value as ImageMetadata).src === 'string'
+ ) {
+ return value as ImageMetadata
+ }
+ return null
+}
diff --git a/src/pages/blog/[...id].astro b/src/pages/blog/[...id].astro
index 3ac0fc2..c0fb61f 100644
--- a/src/pages/blog/[...id].astro
+++ b/src/pages/blog/[...id].astro
@@ -14,10 +14,8 @@ import Layout from '@/layouts/Layout.astro'
import {
getAdjacentPosts,
getAllPostsAndSubposts,
- getCombinedReadingTime,
getParentId,
getParentPost,
- getPostReadingTime,
getSubpostCount,
getTOCSections,
fetchRemotePostContent,
@@ -27,7 +25,7 @@ import {
parseAuthors,
} from '@/lib/data-utils'
import type { TOCSection } from '@/lib/data-utils'
-import { formatDate, readingTime } from '@/lib/utils'
+import { formatDate } from '@/lib/utils'
import { Icon } from 'astro-icon/components'
import { Image } from 'astro:assets'
import { render } from 'astro:content'
@@ -68,13 +66,6 @@ const hasChildPosts = await hasSubposts(currentPostId)
const subpostCount = !isCurrentSubpost
? await getSubpostCount(currentPostId)
: 0
-const postReadingTime = remoteContent
- ? readingTime(remoteContent.wordCount)
- : await getPostReadingTime(currentPostId)
-const combinedReadingTime =
- !remoteContent && hasChildPosts && !isCurrentSubpost
- ? await getCombinedReadingTime(currentPostId)
- : null
const tocSections: TOCSection[] = remoteContent
? remoteContent.headings.length > 0
@@ -87,12 +78,8 @@ const tocSections: TOCSection[] = remoteContent
]
: []
: await getTOCSections(currentPostId)
-const heroImage =
- post.data.banner && typeof post.data.banner === 'object' && 'src' in post.data.banner
- ? post.data.banner
- : post.data.image && typeof post.data.image === 'object' && 'src' in post.data.image
- ? post.data.image
- : null
+const heroImage = post.data.banner
+export const prerender = false;
---
@@ -204,23 +191,6 @@ const heroImage =
{formatDate(post.data.date)}
-
-
- {postReadingTime}
- {
- combinedReadingTime &&
- combinedReadingTime !== postReadingTime && (
-
- {' '}
- ({combinedReadingTime} total)
-
- )
- }
-
-
-
{
subpostCount > 0 && (
diff --git a/src/pages/blog/[...page].astro b/src/pages/blog/[...page].astro
index eb196db..3f60dca 100644
--- a/src/pages/blog/[...page].astro
+++ b/src/pages/blog/[...page].astro
@@ -21,6 +21,7 @@ const { page } = Astro.props
const postsByYear = groupPostsByYear(page.data)
const years = Object.keys(postsByYear).sort((a, b) => parseInt(b) - parseInt(a))
+export const prerender = false;
---
diff --git a/src/pages/tags/[...id].astro b/src/pages/tags/[...id].astro
index dfd4bb8..0b5b269 100644
--- a/src/pages/tags/[...id].astro
+++ b/src/pages/tags/[...id].astro
@@ -24,6 +24,7 @@ export async function getStaticPaths() {
}
const { tag, posts } = Astro.props
+export const prerender = false;
---
diff --git a/src/pages/tags/index.astro b/src/pages/tags/index.astro
index 3d2c75f..7da6a82 100644
--- a/src/pages/tags/index.astro
+++ b/src/pages/tags/index.astro
@@ -8,6 +8,7 @@ import { getSortedTags } from '@/lib/data-utils'
import { Icon } from 'astro-icon/components'
const sortedTags = await getSortedTags()
+export const prerender = false;
---