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..6d91c60 --- /dev/null +++ b/src/components/svelte/BookCard.svelte @@ -0,0 +1,106 @@ + + + + +{#snippet coverImage()} + {#if errored} +
+ + {book.title} +
+ {:else} + {`Cover (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} 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..9cbd2c2 --- /dev/null +++ b/src/data/reading.ts @@ -0,0 +1,189 @@ +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 +} + +// 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 + }, + { + title: 'Operating Systems: Three Easy Pieces', + author: 'Remzi H. Arpaci-Dusseau', + isbn: '9781985086593', + status: 'reading', + cover: osThreeEasyPieces, + progress: 30, + }, + { + title: 'Effective TypeScript', + author: 'Dan Vanderkam', + isbn: '9781492053743', + status: 'reading', + cover: effectiveTypeScript, + progress: 45, + }, + + // 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..b66eeb6 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,23 @@ 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, + // 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. +

+
+ +
+ +
+
+