Optimizations + minor cleanups
All checks were successful
Docker Deploy / build-and-push (push) Successful in 4m15s

This commit is contained in:
2025-12-18 00:30:01 -07:00
parent 203f83bfcb
commit 70ee8a2c42
11 changed files with 190 additions and 227 deletions

View File

@@ -1,10 +1,5 @@
import { Icon } from "astro-icon/components";
import type {
IconType,
LucideIcon,
AstroIconName,
CustomIconComponent,
} from "../types";
import type { IconType, LucideIcon, AstroIconName } from "../types";
interface IconRendererProps {
icon: IconType;
@@ -22,10 +17,6 @@ function isAstroIconName(icon: IconType): icon is AstroIconName {
return typeof icon === "string";
}
function isCustomComponent(icon: IconType): icon is CustomIconComponent {
return typeof icon === "function" && !isLucideIcon(icon);
}
export default function IconRenderer({
icon,
size,
@@ -41,11 +32,10 @@ export default function IconRenderer({
return <Icon name={icon} class={className} {...props} />;
}
if (isCustomComponent(icon)) {
if (typeof icon === "function") {
const CustomComponent = icon;
return <CustomComponent class={className} {...props} />;
}
// Fallback
return null;
}

View File

@@ -32,26 +32,15 @@ export default function NavigationBar({ currentPath }: NavigationBarProps) {
}
};
// Set initial path
updatePath();
// Listen for Astro's view transition events
const handleAstroNavigation = () => {
updatePath();
};
// Listen for astro:page-load event which fires after navigation completes
document.addEventListener("astro:page-load", handleAstroNavigation);
// Also listen for astro:after-swap as a backup
document.addEventListener("astro:after-swap", handleAstroNavigation);
// Listen for regular navigation events as fallback
document.addEventListener("astro:page-load", updatePath);
document.addEventListener("astro:after-swap", updatePath);
window.addEventListener("popstate", updatePath);
return () => {
document.removeEventListener("astro:page-load", handleAstroNavigation);
document.removeEventListener("astro:after-swap", handleAstroNavigation);
document.removeEventListener("astro:page-load", updatePath);
document.removeEventListener("astro:after-swap", updatePath);
window.removeEventListener("popstate", updatePath);
};
}, []);

View File

@@ -61,7 +61,7 @@ export default function ResumeDownloadButton({
Generating PDF...
</>
) : (
<>Download Resume</>
"Download Resume"
)}
</button>
{error && <div class="mt-2 text-error text-sm">{error}</div>}

View File

@@ -218,8 +218,8 @@ export default function ResumeSettingsModal({
role="tab"
class={`px-4 py-2 rounded-full text-sm font-bold transition-all duration-200 ${
activeTab === "edit"
? "btn btn-primary font-bold shadow-sm"
: "text-base-content/70 hover:text-base-content font-bold hover:bg-base-200"
? "btn btn-primary shadow-sm"
: "text-base-content/70 hover:text-base-content hover:bg-base-200"
}`}
onClick={() => setActiveTab("edit")}
>

View File

@@ -9,7 +9,7 @@ I change what I use _constantly_ in order to find something that feels just
right. I wanted to share them here and update them here so when someone asks, I
can just point them to this article.
1. VSCodium - The joys of VSCode and its extension ecosystem, but without Microsoft's insane amount of tracking and AI slop
1. Zed - Zed is a performany open-source code editor that allows you to configure away all of the AI and signin features. Performs well and has its own extensive extension ecosystem. My config to clean it up can be found [here](https://git.atri.dad/atridad/zed-config).
3. Ghostty - A Zig based terminal emulator by one of the founders of Hashicorp. Runs great on MacOS and Linux. No windows for those who are into that.
4. Bitwarden - An open-source password manager. Easy to self host with Vaultwarden and with the recent updates, it has SSH Agent support!
5. iA Writer - A minimalist Markdown editor. For MacOS and Windows only, but really the MacOS version is the most mature. Awesome for focus.

View File

@@ -6,23 +6,14 @@ export const GET: APIRoute = async () => {
const posts = await getCollection('posts');
// Get the raw content from each post
const postsWithContent = await Promise.all(
posts.map(async (post) => {
const { Content } = await post.render();
// Get the raw markdown content by reading the file
const rawContent = post.body;
return {
slug: post.slug,
title: post.data.title,
description: post.data.description,
pubDate: post.data.pubDate.toISOString().split('T')[0],
tags: post.data.tags || [],
content: rawContent
};
})
);
const postsWithContent = posts.map((post) => ({
slug: post.slug,
title: post.data.title,
description: post.data.description,
pubDate: post.data.pubDate.toISOString().split('T')[0],
tags: post.data.tags || [],
content: post.body
}));
return new Response(JSON.stringify(postsWithContent), {
status: 200,

View File

@@ -3,7 +3,6 @@ import { config } from "../../../config";
import * as TOML from "@iarna/toml";
import { renderToStream } from "@react-pdf/renderer";
import { ResumeDocument } from "../../../pdf/ResumeDocument";
import React from "react";
async function getSimpleIconPath(iconName: string): Promise<string> {
try {

View File

@@ -1,5 +1,4 @@
/** @jsxImportSource react */
import React from "react";
import {
Document,
Page,
@@ -9,7 +8,6 @@ import {
Link,
Svg,
Path,
Font,
} from "@react-pdf/renderer";
const styles = StyleSheet.create({

View File

@@ -92,29 +92,25 @@ export async function fetchGiteaInfoFromUrl(
return fetchGiteaRepoInfo(config);
}
const MINUTE_MS = 60_000;
const HOUR_MS = 3_600_000;
const DAY_MS = 86_400_000;
const pluralize = (n: number, unit: string) => `${n} ${unit}${n !== 1 ? "s" : ""} ago`;
export function formatRelativeTime(dateString: string): string {
if (!dateString) return "Unknown";
const date = new Date(dateString);
const now = new Date();
const diffMs = now.getTime() - date.getTime();
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
const diffMinutes = Math.floor(diffMs / (1000 * 60));
const diffMs = Date.now() - new Date(dateString).getTime();
const diffMinutes = Math.floor(diffMs / MINUTE_MS);
const diffHours = Math.floor(diffMs / HOUR_MS);
const diffDays = Math.floor(diffMs / DAY_MS);
if (diffMinutes < 60) {
return `${diffMinutes} minute${diffMinutes !== 1 ? "s" : ""} ago`;
} else if (diffHours < 24) {
return `${diffHours} hour${diffHours !== 1 ? "s" : ""} ago`;
} else if (diffDays < 30) {
return `${diffDays} day${diffDays !== 1 ? "s" : ""} ago`;
} else if (diffDays < 365) {
const months = Math.floor(diffDays / 30);
return `${months} month${months !== 1 ? "s" : ""} ago`;
} else {
const years = Math.floor(diffDays / 365);
return `${years} year${years !== 1 ? "s" : ""} ago`;
}
if (diffMinutes < 60) return pluralize(diffMinutes, "minute");
if (diffHours < 24) return pluralize(diffHours, "hour");
if (diffDays < 30) return pluralize(diffDays, "day");
if (diffDays < 365) return pluralize(Math.floor(diffDays / 30), "month");
return pluralize(Math.floor(diffDays / 365), "year");
}
export function formatRepoSize(sizeKb: number): string {