diff --git a/src/assets/reading/algorithms-to-live-by.jpg b/src/assets/reading/algorithms-to-live-by.jpg
new file mode 100644
index 0000000..6e3c2c7
Binary files /dev/null and b/src/assets/reading/algorithms-to-live-by.jpg differ
diff --git a/src/assets/reading/architecture-patterns-with-python.jpg b/src/assets/reading/architecture-patterns-with-python.jpg
new file mode 100644
index 0000000..e18dfdf
Binary files /dev/null and b/src/assets/reading/architecture-patterns-with-python.jpg differ
diff --git a/src/assets/reading/clean-code.jpg b/src/assets/reading/clean-code.jpg
new file mode 100644
index 0000000..7889e2f
Binary files /dev/null and b/src/assets/reading/clean-code.jpg differ
diff --git a/src/assets/reading/code-petzold.jpg b/src/assets/reading/code-petzold.jpg
new file mode 100644
index 0000000..4aa8ca2
Binary files /dev/null and b/src/assets/reading/code-petzold.jpg differ
diff --git a/src/assets/reading/devops-handbook.jpg b/src/assets/reading/devops-handbook.jpg
new file mode 100644
index 0000000..34593db
Binary files /dev/null and b/src/assets/reading/devops-handbook.jpg differ
diff --git a/src/assets/reading/docker-up-and-running.jpg b/src/assets/reading/docker-up-and-running.jpg
new file mode 100644
index 0000000..cc3d4ed
Binary files /dev/null and b/src/assets/reading/docker-up-and-running.jpg differ
diff --git a/src/assets/reading/docs-for-developers.jpg b/src/assets/reading/docs-for-developers.jpg
new file mode 100644
index 0000000..11c92c0
Binary files /dev/null and b/src/assets/reading/docs-for-developers.jpg differ
diff --git a/src/assets/reading/effective-typescript.jpg b/src/assets/reading/effective-typescript.jpg
new file mode 100644
index 0000000..c0d6dfe
Binary files /dev/null and b/src/assets/reading/effective-typescript.jpg differ
diff --git a/src/assets/reading/fastapi.jpg b/src/assets/reading/fastapi.jpg
new file mode 100644
index 0000000..7ff43f4
Binary files /dev/null and b/src/assets/reading/fastapi.jpg differ
diff --git a/src/assets/reading/fluent-python.jpg b/src/assets/reading/fluent-python.jpg
new file mode 100644
index 0000000..fb67c26
Binary files /dev/null and b/src/assets/reading/fluent-python.jpg differ
diff --git a/src/assets/reading/head-first-design-patterns.jpg b/src/assets/reading/head-first-design-patterns.jpg
new file mode 100644
index 0000000..daef180
Binary files /dev/null and b/src/assets/reading/head-first-design-patterns.jpg differ
diff --git a/src/assets/reading/head-first-software-development.jpg b/src/assets/reading/head-first-software-development.jpg
new file mode 100644
index 0000000..cb74575
Binary files /dev/null and b/src/assets/reading/head-first-software-development.jpg differ
diff --git a/src/assets/reading/just-for-fun.jpg b/src/assets/reading/just-for-fun.jpg
new file mode 100644
index 0000000..4904864
Binary files /dev/null and b/src/assets/reading/just-for-fun.jpg differ
diff --git a/src/assets/reading/oo-thought-process.jpg b/src/assets/reading/oo-thought-process.jpg
new file mode 100644
index 0000000..eacf1db
Binary files /dev/null and b/src/assets/reading/oo-thought-process.jpg differ
diff --git a/src/assets/reading/os-three-easy-pieces.jpg b/src/assets/reading/os-three-easy-pieces.jpg
new file mode 100644
index 0000000..6beeed4
Binary files /dev/null and b/src/assets/reading/os-three-easy-pieces.jpg differ
diff --git a/src/assets/reading/personal-mba.jpg b/src/assets/reading/personal-mba.jpg
new file mode 100644
index 0000000..a34e8c0
Binary files /dev/null and b/src/assets/reading/personal-mba.jpg differ
diff --git a/src/assets/reading/philosophy-of-software-design.jpg b/src/assets/reading/philosophy-of-software-design.jpg
new file mode 100644
index 0000000..d4c608d
Binary files /dev/null and b/src/assets/reading/philosophy-of-software-design.jpg differ
diff --git a/src/assets/reading/refactoring.jpg b/src/assets/reading/refactoring.jpg
new file mode 100644
index 0000000..1aeab8b
Binary files /dev/null and b/src/assets/reading/refactoring.jpg differ
diff --git a/src/assets/reading/the-c-programming-language.jpg b/src/assets/reading/the-c-programming-language.jpg
new file mode 100644
index 0000000..a0d92ff
Binary files /dev/null and b/src/assets/reading/the-c-programming-language.jpg differ
diff --git a/src/assets/reading/theory-of-computation.jpg b/src/assets/reading/theory-of-computation.jpg
new file mode 100644
index 0000000..573c094
Binary files /dev/null and b/src/assets/reading/theory-of-computation.jpg differ
diff --git a/src/components/svelte/BookCard.svelte b/src/components/svelte/BookCard.svelte
new file mode 100644
index 0000000..ef88fbc
--- /dev/null
+++ b/src/components/svelte/BookCard.svelte
@@ -0,0 +1,111 @@
+
+
+
+
+{#snippet coverImage()}
+ {#if errored}
+
+
+ {book.title}
+
+ {:else}
+
(errored = true)}
+ />
+ {/if}
+{/snippet}
+
+{#snippet progressBar()}
+ {#if showProgress}
+
+
+
+ {book.progress}%
+
+
+ {/if}
+{/snippet}
+
+{#if variant === 'row'}
+
+
+ {@render coverImage()}
+
+
+
{book.title}
+ {#if book.author}
+
{book.author}
+ {/if}
+ {@render progressBar()}
+
+
+{:else}
+
+
+ {@render coverImage()}
+
+
+
{book.title}
+ {#if book.author}
+
{book.author}
+ {/if}
+ {@render progressBar()}
+ {#if book.note}
+
{book.note}
+ {/if}
+
+
+{/if}
diff --git a/src/components/svelte/ReadingList.svelte b/src/components/svelte/ReadingList.svelte
new file mode 100644
index 0000000..4120527
--- /dev/null
+++ b/src/components/svelte/ReadingList.svelte
@@ -0,0 +1,45 @@
+
+
+
+ {#if currentlyReading.length > 0}
+
+
+ Currently Reading
+
+
+ {#each currentlyReading as book (book.isbn)}
+
+ {/each}
+
+
+ {/if}
+
+ {#if finished.length > 0}
+
+
+ Finished
+
+
+ {#each finished as book (book.isbn)}
+
+ {/each}
+
+
+ {/if}
+
diff --git a/src/consts.ts b/src/consts.ts
index 643310c..10c2d1d 100644
--- a/src/consts.ts
+++ b/src/consts.ts
@@ -24,6 +24,10 @@ export const NAV_LINKS: SocialLink[] = [
href: '/blog',
label: 'blog',
},
+ {
+ href: '/reading',
+ label: 'reading',
+ },
{
href: '/#contact',
label: 'contact',
diff --git a/src/data/reading.ts b/src/data/reading.ts
new file mode 100644
index 0000000..2336740
--- /dev/null
+++ b/src/data/reading.ts
@@ -0,0 +1,194 @@
+import type { ImageMetadata } from 'astro'
+
+import devopsHandbook from '@/assets/reading/devops-handbook.jpg'
+import osThreeEasyPieces from '@/assets/reading/os-three-easy-pieces.jpg'
+import cleanCode from '@/assets/reading/clean-code.jpg'
+import refactoring from '@/assets/reading/refactoring.jpg'
+import philosophyOfSoftwareDesign from '@/assets/reading/philosophy-of-software-design.jpg'
+import architecturePatternsWithPython from '@/assets/reading/architecture-patterns-with-python.jpg'
+import fluentPython from '@/assets/reading/fluent-python.jpg'
+import effectiveTypeScript from '@/assets/reading/effective-typescript.jpg'
+import headFirstDesignPatterns from '@/assets/reading/head-first-design-patterns.jpg'
+import ooThoughtProcess from '@/assets/reading/oo-thought-process.jpg'
+import dockerUpAndRunning from '@/assets/reading/docker-up-and-running.jpg'
+import theCProgrammingLanguage from '@/assets/reading/the-c-programming-language.jpg'
+import codePetzold from '@/assets/reading/code-petzold.jpg'
+import theoryOfComputation from '@/assets/reading/theory-of-computation.jpg'
+import headFirstSoftwareDevelopment from '@/assets/reading/head-first-software-development.jpg'
+import docsForDevelopers from '@/assets/reading/docs-for-developers.jpg'
+import justForFun from '@/assets/reading/just-for-fun.jpg'
+import algorithmsToLiveBy from '@/assets/reading/algorithms-to-live-by.jpg'
+import personalMba from '@/assets/reading/personal-mba.jpg'
+import fastAPI from '@/assets/reading/fastapi.jpg'
+
+export interface ReadingEntry {
+ title: string
+ author?: string
+ isbn: string
+ status: 'reading' | 'finished'
+ /**
+ * Local cover image (optimized by Astro). When omitted, the cover falls back
+ * to the Open Library Covers API by ISBN, and then to a title/author card if
+ * that's unavailable too. To add a missing cover, drop a JPG in
+ * src/assets/reading/, import it above, and set it here.
+ */
+ cover?: ImageMetadata
+ /** Reading progress, 0–100. Only shown for `status: 'reading'`. */
+ progress?: number
+ /** Short one-liner (why you're reading it). Only shown on the /reading page. */
+ note?: string
+}
+
+// Order matters: books render in the order listed here, curated by relevance
+// (most relevant to my work first).
+export const reading: ReadingEntry[] = [
+ // Currently reading
+ {
+ title: 'The DevOps Handbook',
+ author: 'Gene Kim',
+ isbn: '9781942788003',
+ status: 'reading',
+ cover: devopsHandbook,
+ progress: 55, // update as you read
+ note: 'Sharpening the ops side of my full-stack work.',
+ },
+ {
+ title: 'Operating Systems: Three Easy Pieces',
+ author: 'Remzi H. Arpaci-Dusseau',
+ isbn: '9781985086593',
+ status: 'reading',
+ cover: osThreeEasyPieces,
+ progress: 30,
+ note: 'Filling in OS fundamentals — the free classic.',
+ },
+ {
+ title: 'Effective TypeScript',
+ author: 'Dan Vanderkam',
+ isbn: '9781492053743',
+ status: 'reading',
+ cover: effectiveTypeScript,
+ progress: 45,
+ note: 'Leveling up the TypeScript I use across this site.',
+ },
+
+ // Finished — ordered by relevance
+ {
+ title: 'Clean Code',
+ author: 'Robert C. Martin',
+ isbn: '9780132350884',
+ status: 'finished',
+ cover: cleanCode,
+ },
+ {
+ title: 'Refactoring',
+ author: 'Martin Fowler',
+ isbn: '9780134757599',
+ status: 'finished',
+ cover: refactoring,
+ },
+ {
+ title: 'A Philosophy of Software Design',
+ author: 'John Ousterhout',
+ isbn: '9781732102200',
+ status: 'finished',
+ cover: philosophyOfSoftwareDesign,
+ },
+ {
+ title: 'Architecture Patterns with Python',
+ author: 'Harry Percival, Bob Gregory',
+ isbn: '9781492052203',
+ status: 'finished',
+ cover: architecturePatternsWithPython,
+ },
+ {
+ title: 'Fluent Python',
+ author: 'Luciano Ramalho',
+ isbn: '9781492056355',
+ status: 'finished',
+ cover: fluentPython,
+ },
+ {
+ title: 'Head First Design Patterns',
+ author: 'Eric Freeman',
+ isbn: '9781492078005',
+ status: 'finished',
+ cover: headFirstDesignPatterns,
+ },
+ {
+ title: 'The Object-Oriented Thought Process',
+ author: 'Matt Weisfeld',
+ isbn: '9780321861276',
+ status: 'finished',
+ cover: ooThoughtProcess,
+ },
+ {
+ title: 'Docker: Up & Running',
+ author: 'Sean Kane, Karl Matthias',
+ isbn: '9781098131821',
+ status: 'finished',
+ cover: dockerUpAndRunning,
+ },
+ {
+ title: 'FastAPI',
+ author: 'Bill Lubanovic',
+ isbn: '9781098135508',
+ status: 'finished',
+ cover: fastAPI,
+ },
+ {
+ title: 'The C Programming Language',
+ author: 'Brian Kernighan, Dennis Ritchie',
+ isbn: '9780131103627',
+ status: 'finished',
+ cover: theCProgrammingLanguage,
+ },
+ {
+ title: 'Code: The Hidden Language of Computer Hardware and Software',
+ author: 'Charles Petzold',
+ isbn: '9780137909100',
+ status: 'finished',
+ cover: codePetzold,
+ },
+ {
+ title: 'Introduction to the Theory of Computation',
+ author: 'Michael Sipser',
+ isbn: '9781133187790',
+ status: 'finished',
+ cover: theoryOfComputation,
+ },
+ {
+ title: 'Head First Software Development',
+ author: 'Dan Pilone',
+ isbn: '9780596527358',
+ status: 'finished',
+ cover: headFirstSoftwareDevelopment,
+ },
+ {
+ title: 'Docs for Developers',
+ author: 'Jared Bhatti',
+ isbn: '9781484272169',
+ status: 'finished',
+ cover: docsForDevelopers,
+ },
+ {
+ title: 'Just for Fun',
+ author: 'Linus Torvalds, David Diamond',
+ isbn: '9780066620732',
+ status: 'finished',
+ cover: justForFun,
+ },
+ {
+ title: 'Algorithms to Live By',
+ author: 'Brian Christian, Tom Griffiths',
+ isbn: '9781627790369',
+ status: 'finished',
+ cover: algorithmsToLiveBy,
+ },
+ {
+ title: 'The Personal MBA',
+ author: 'Josh Kaufman',
+ isbn: '9781591845577',
+ status: 'finished',
+ cover: personalMba,
+ },
+]
diff --git a/src/lib/data-utils.ts b/src/lib/data-utils.ts
index f59a7c6..f065c60 100644
--- a/src/lib/data-utils.ts
+++ b/src/lib/data-utils.ts
@@ -1,4 +1,7 @@
import { getCollection, type CollectionEntry } from 'astro:content'
+import { getImage } from 'astro:assets'
+import { reading } from '@/data/reading'
+import type { Book } from '@/components/svelte/BookCard.svelte'
export async function getAllPosts(): Promise[]> {
const posts = await getCollection('blog')
@@ -92,6 +95,24 @@ export async function getFeaturedProjects(): Promise
return projects.slice(0, 4)
}
+export async function getReadingList(): Promise {
+ return Promise.all(
+ reading.map(async (entry) => ({
+ title: entry.title,
+ author: entry.author,
+ isbn: entry.isbn,
+ status: entry.status,
+ progress: entry.progress,
+ note: entry.note,
+ // Local covers get Astro-optimized to WebP; everything else falls back to
+ // the Open Library Covers API by ISBN (and then a title/author card).
+ coverSrc: entry.cover
+ ? (await getImage({ src: entry.cover, width: 400, format: 'webp' })).src
+ : `https://covers.openlibrary.org/b/isbn/${entry.isbn}-L.jpg?default=false`,
+ })),
+ )
+}
+
export async function getProjectsFeaturedTags(maxCount: number): Promise {
const projects = await getAllProjects()
const tags = new Set()
diff --git a/src/pages/index.astro b/src/pages/index.astro
index 4956338..d48bf47 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -6,7 +6,7 @@ import Skills from '../components/svelte/Skills.svelte'
import { buttonVariants } from '@/lib/button-variants'
import Logo from '../components/svelte/Logo.svelte'
import Layout from '@/layouts/Layout.astro'
-import { getRecentPosts, getFeaturedProjects } from '@/lib/data-utils'
+import { getRecentPosts, getFeaturedProjects, getReadingList } from '@/lib/data-utils'
import { SITE } from '@/consts'
import ProjectCard from '../components/svelte/ProjectCard.svelte'
import IconGit from '~icons/simple-icons/git'
@@ -19,9 +19,14 @@ import EducationItem from '../components/svelte/EducationItem.svelte'
import { education } from '@/data/education'
import ContactForm from '../components/svelte/ContactForm.svelte'
import IconMail from '~icons/lucide/mail'
+import IconBookOpen from '~icons/lucide/book-open'
+import BookCard from '../components/svelte/BookCard.svelte'
const blog = await getRecentPosts(3)
const featuredProjects = await getFeaturedProjects()
+const currentlyReading = (await getReadingList()).filter(
+ (book) => book.status === 'reading',
+)
const currentUrl = Astro.url;
const turnstileSitekey = import.meta.env.PUBLIC_TURNSTILE_SITEKEY
---
@@ -157,6 +162,39 @@ const turnstileSitekey = import.meta.env.PUBLIC_TURNSTILE_SITEKEY
+ {currentlyReading.length > 0 && (
+
+
+
+
+ Currently Reading
+
+
+ What's on my desk right now.
+
+
+
+ {currentlyReading.map((book) => (
+
+ ))}
+
+
+
+ View full reading list →
+
+
+
+ )}
+
+
+
+
+
+
+
+
+
+ What I'm reading
+
+
+
Reading List
+
+ Books I'm working through and ones I've finished — mostly software
+ engineering and computer science, with the occasional detour.
+
+
+
+
+
+
+
+