🏗 Restructure project, change font to Maple Mono
@@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
"$ref": "#/definitions/blog",
|
|
||||||
"definitions": {
|
|
||||||
"blog": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"title": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"pubDate": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"tags": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"$schema": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"title",
|
|
||||||
"pubDate",
|
|
||||||
"tags"
|
|
||||||
],
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
|
||||||
}
|
|
||||||
@@ -1 +1,10 @@
|
|||||||
export default new Map();
|
|
||||||
|
import __ASTRO_IMAGE_IMPORT_3KcDr from "src/assets/blog/my-ai-portrait.jpg?astroContentImageFlag=&importer=src%2Fcontent%2Fblog%2F2022%2Fcreating-a-dating-profile-with-ai.md";
|
||||||
|
import __ASTRO_IMAGE_IMPORT_1G57ng from "src/assets/blog/ileopard/mac-os-10-1.png?astroContentImageFlag=&importer=src%2Fcontent%2Fblog%2F2022%2Fileopard-a-retrospective.md";
|
||||||
|
import __ASTRO_IMAGE_IMPORT_Z1ESWoO from "src/assets/blog/ileopard/itunes-7.gif?astroContentImageFlag=&importer=src%2Fcontent%2Fblog%2F2022%2Fileopard-a-retrospective.md";
|
||||||
|
import __ASTRO_IMAGE_IMPORT_rrnp from "src/assets/blog/ileopard/ileopard-2-0-1.png?astroContentImageFlag=&importer=src%2Fcontent%2Fblog%2F2022%2Fileopard-a-retrospective.md";
|
||||||
|
import __ASTRO_IMAGE_IMPORT_Zi2DqH from "src/assets/blog/gunpla/box.jpg?astroContentImageFlag=&importer=src%2Fcontent%2Fblog%2F2023%2Fmy-gunpla-adventure.md";
|
||||||
|
import __ASTRO_IMAGE_IMPORT_1OkzEl from "src/assets/blog/gunpla/all-the-parts.jpg?astroContentImageFlag=&importer=src%2Fcontent%2Fblog%2F2023%2Fmy-gunpla-adventure.md";
|
||||||
|
import __ASTRO_IMAGE_IMPORT_FYQiW from "src/assets/blog/gunpla/final.jpg?astroContentImageFlag=&importer=src%2Fcontent%2Fblog%2F2023%2Fmy-gunpla-adventure.md";
|
||||||
|
export default new Map([["src/assets/blog/my-ai-portrait.jpg?astroContentImageFlag=&importer=src%2Fcontent%2Fblog%2F2022%2Fcreating-a-dating-profile-with-ai.md", __ASTRO_IMAGE_IMPORT_3KcDr], ["src/assets/blog/ileopard/mac-os-10-1.png?astroContentImageFlag=&importer=src%2Fcontent%2Fblog%2F2022%2Fileopard-a-retrospective.md", __ASTRO_IMAGE_IMPORT_1G57ng], ["src/assets/blog/ileopard/itunes-7.gif?astroContentImageFlag=&importer=src%2Fcontent%2Fblog%2F2022%2Fileopard-a-retrospective.md", __ASTRO_IMAGE_IMPORT_Z1ESWoO], ["src/assets/blog/ileopard/ileopard-2-0-1.png?astroContentImageFlag=&importer=src%2Fcontent%2Fblog%2F2022%2Fileopard-a-retrospective.md", __ASTRO_IMAGE_IMPORT_rrnp], ["src/assets/blog/gunpla/box.jpg?astroContentImageFlag=&importer=src%2Fcontent%2Fblog%2F2023%2Fmy-gunpla-adventure.md", __ASTRO_IMAGE_IMPORT_Zi2DqH], ["src/assets/blog/gunpla/all-the-parts.jpg?astroContentImageFlag=&importer=src%2Fcontent%2Fblog%2F2023%2Fmy-gunpla-adventure.md", __ASTRO_IMAGE_IMPORT_1OkzEl], ["src/assets/blog/gunpla/final.jpg?astroContentImageFlag=&importer=src%2Fcontent%2Fblog%2F2023%2Fmy-gunpla-adventure.md", __ASTRO_IMAGE_IMPORT_FYQiW]]);
|
||||||
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
declare module 'astro:content' {
|
|
||||||
interface Render {
|
|
||||||
'.mdx': Promise<{
|
|
||||||
Content: import('astro').MarkdownInstance<{}>['Content'];
|
|
||||||
headings: import('astro').MarkdownHeading[];
|
|
||||||
remarkPluginFrontmatter: Record<string, any>;
|
|
||||||
components: import('astro').MDXInstance<{}>['components'];
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module 'astro:content' {
|
|
||||||
export interface RenderResult {
|
|
||||||
Content: import('astro/runtime/server/index.js').AstroComponentFactory;
|
|
||||||
headings: import('astro').MarkdownHeading[];
|
|
||||||
remarkPluginFrontmatter: Record<string, any>;
|
|
||||||
}
|
|
||||||
interface Render {
|
|
||||||
'.md': Promise<RenderResult>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RenderedContent {
|
|
||||||
html: string;
|
|
||||||
metadata?: {
|
|
||||||
imagePaths: Array<string>;
|
|
||||||
[key: string]: unknown;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module 'astro:content' {
|
|
||||||
type Flatten<T> = T extends { [K: string]: infer U } ? U : never;
|
|
||||||
|
|
||||||
export type CollectionKey = keyof AnyEntryMap;
|
|
||||||
export type CollectionEntry<C extends CollectionKey> = Flatten<AnyEntryMap[C]>;
|
|
||||||
|
|
||||||
export type ContentCollectionKey = keyof ContentEntryMap;
|
|
||||||
export type DataCollectionKey = keyof DataEntryMap;
|
|
||||||
|
|
||||||
type AllValuesOf<T> = T extends any ? T[keyof T] : never;
|
|
||||||
type ValidContentEntrySlug<C extends keyof ContentEntryMap> = AllValuesOf<
|
|
||||||
ContentEntryMap[C]
|
|
||||||
>['slug'];
|
|
||||||
|
|
||||||
/** @deprecated Use `getEntry` instead. */
|
|
||||||
export function getEntryBySlug<
|
|
||||||
C extends keyof ContentEntryMap,
|
|
||||||
E extends ValidContentEntrySlug<C> | (string & {}),
|
|
||||||
>(
|
|
||||||
collection: C,
|
|
||||||
// Note that this has to accept a regular string too, for SSR
|
|
||||||
entrySlug: E,
|
|
||||||
): E extends ValidContentEntrySlug<C>
|
|
||||||
? Promise<CollectionEntry<C>>
|
|
||||||
: Promise<CollectionEntry<C> | undefined>;
|
|
||||||
|
|
||||||
/** @deprecated Use `getEntry` instead. */
|
|
||||||
export function getDataEntryById<C extends keyof DataEntryMap, E extends keyof DataEntryMap[C]>(
|
|
||||||
collection: C,
|
|
||||||
entryId: E,
|
|
||||||
): Promise<CollectionEntry<C>>;
|
|
||||||
|
|
||||||
export function getCollection<C extends keyof AnyEntryMap, E extends CollectionEntry<C>>(
|
|
||||||
collection: C,
|
|
||||||
filter?: (entry: CollectionEntry<C>) => entry is E,
|
|
||||||
): Promise<E[]>;
|
|
||||||
export function getCollection<C extends keyof AnyEntryMap>(
|
|
||||||
collection: C,
|
|
||||||
filter?: (entry: CollectionEntry<C>) => unknown,
|
|
||||||
): Promise<CollectionEntry<C>[]>;
|
|
||||||
|
|
||||||
export function getEntry<
|
|
||||||
C extends keyof ContentEntryMap,
|
|
||||||
E extends ValidContentEntrySlug<C> | (string & {}),
|
|
||||||
>(entry: {
|
|
||||||
collection: C;
|
|
||||||
slug: E;
|
|
||||||
}): E extends ValidContentEntrySlug<C>
|
|
||||||
? Promise<CollectionEntry<C>>
|
|
||||||
: Promise<CollectionEntry<C> | undefined>;
|
|
||||||
export function getEntry<
|
|
||||||
C extends keyof DataEntryMap,
|
|
||||||
E extends keyof DataEntryMap[C] | (string & {}),
|
|
||||||
>(entry: {
|
|
||||||
collection: C;
|
|
||||||
id: E;
|
|
||||||
}): E extends keyof DataEntryMap[C]
|
|
||||||
? Promise<DataEntryMap[C][E]>
|
|
||||||
: Promise<CollectionEntry<C> | undefined>;
|
|
||||||
export function getEntry<
|
|
||||||
C extends keyof ContentEntryMap,
|
|
||||||
E extends ValidContentEntrySlug<C> | (string & {}),
|
|
||||||
>(
|
|
||||||
collection: C,
|
|
||||||
slug: E,
|
|
||||||
): E extends ValidContentEntrySlug<C>
|
|
||||||
? Promise<CollectionEntry<C>>
|
|
||||||
: Promise<CollectionEntry<C> | undefined>;
|
|
||||||
export function getEntry<
|
|
||||||
C extends keyof DataEntryMap,
|
|
||||||
E extends keyof DataEntryMap[C] | (string & {}),
|
|
||||||
>(
|
|
||||||
collection: C,
|
|
||||||
id: E,
|
|
||||||
): E extends keyof DataEntryMap[C]
|
|
||||||
? string extends keyof DataEntryMap[C]
|
|
||||||
? Promise<DataEntryMap[C][E]> | undefined
|
|
||||||
: Promise<DataEntryMap[C][E]>
|
|
||||||
: Promise<CollectionEntry<C> | undefined>;
|
|
||||||
|
|
||||||
/** Resolve an array of entry references from the same collection */
|
|
||||||
export function getEntries<C extends keyof ContentEntryMap>(
|
|
||||||
entries: {
|
|
||||||
collection: C;
|
|
||||||
slug: ValidContentEntrySlug<C>;
|
|
||||||
}[],
|
|
||||||
): Promise<CollectionEntry<C>[]>;
|
|
||||||
export function getEntries<C extends keyof DataEntryMap>(
|
|
||||||
entries: {
|
|
||||||
collection: C;
|
|
||||||
id: keyof DataEntryMap[C];
|
|
||||||
}[],
|
|
||||||
): Promise<CollectionEntry<C>[]>;
|
|
||||||
|
|
||||||
export function render<C extends keyof AnyEntryMap>(
|
|
||||||
entry: AnyEntryMap[C][string],
|
|
||||||
): Promise<RenderResult>;
|
|
||||||
|
|
||||||
export function reference<C extends keyof AnyEntryMap>(
|
|
||||||
collection: C,
|
|
||||||
): import('astro/zod').ZodEffects<
|
|
||||||
import('astro/zod').ZodString,
|
|
||||||
C extends keyof ContentEntryMap
|
|
||||||
? {
|
|
||||||
collection: C;
|
|
||||||
slug: ValidContentEntrySlug<C>;
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
collection: C;
|
|
||||||
id: keyof DataEntryMap[C];
|
|
||||||
}
|
|
||||||
>;
|
|
||||||
// Allow generic `string` to avoid excessive type errors in the config
|
|
||||||
// if `dev` is not running to update as you edit.
|
|
||||||
// Invalid collection names will be caught at build time.
|
|
||||||
export function reference<C extends string>(
|
|
||||||
collection: C,
|
|
||||||
): import('astro/zod').ZodEffects<import('astro/zod').ZodString, never>;
|
|
||||||
|
|
||||||
type ReturnTypeOrOriginal<T> = T extends (...args: any[]) => infer R ? R : T;
|
|
||||||
type InferEntrySchema<C extends keyof AnyEntryMap> = import('astro/zod').infer<
|
|
||||||
ReturnTypeOrOriginal<Required<ContentConfig['collections'][C]>['schema']>
|
|
||||||
>;
|
|
||||||
|
|
||||||
type ContentEntryMap = {
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
type DataEntryMap = {
|
|
||||||
"blog": Record<string, {
|
|
||||||
id: string;
|
|
||||||
render(): Render[".md"];
|
|
||||||
slug: string;
|
|
||||||
body: string;
|
|
||||||
collection: "blog";
|
|
||||||
data: InferEntrySchema<"blog">;
|
|
||||||
rendered?: RenderedContent;
|
|
||||||
filePath?: string;
|
|
||||||
}>;
|
|
||||||
"vinyl": Record<string, {
|
|
||||||
id: string;
|
|
||||||
body?: string;
|
|
||||||
collection: "vinyl";
|
|
||||||
data: any;
|
|
||||||
rendered?: RenderedContent;
|
|
||||||
filePath?: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
type AnyEntryMap = ContentEntryMap & DataEntryMap;
|
|
||||||
|
|
||||||
export type ContentConfig = typeof import("../src/content/config.js");
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
// Automatically generated by astro-icon
|
|
||||||
// 001a1ccebb4bfa874cce071296b7167baf197ac1b43baf1f725af5a705835151
|
|
||||||
|
|
||||||
declare module 'virtual:astro-icon' {
|
|
||||||
export type Icon =
|
|
||||||
| "calendar"
|
|
||||||
| "clock"
|
|
||||||
| "pen"
|
|
||||||
| "person";
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"_variables": {
|
"_variables": {
|
||||||
"lastUpdateCheck": 1753720357171
|
"lastUpdateCheck": 1756244081510
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,2 +1 @@
|
|||||||
/// <reference types="astro/client" />
|
/// <reference types="astro/client" />
|
||||||
/// <reference path="content.d.ts" />
|
|
||||||
@@ -10,9 +10,6 @@ export default defineConfig({
|
|||||||
site: 'https://ghall.space',
|
site: 'https://ghall.space',
|
||||||
output: 'static',
|
output: 'static',
|
||||||
integrations: [mdx(), alpinejs()],
|
integrations: [mdx(), alpinejs()],
|
||||||
experimental: {
|
|
||||||
svg: true,
|
|
||||||
},
|
|
||||||
redirects: {
|
redirects: {
|
||||||
'/posts/[...slug]': '/blog/[...slug]',
|
'/posts/[...slug]': '/blog/[...slug]',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -12,13 +12,13 @@
|
|||||||
"lint": "eslint --ext .js --ext .ts --ext .astro"
|
"lint": "eslint --ext .js --ext .ts --ext .astro"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/alpinejs": "^0.4.3",
|
"@astrojs/alpinejs": "^0.4.8",
|
||||||
"@astrojs/mdx": "4.0.2",
|
"@astrojs/mdx": "^4.3.4",
|
||||||
"@astrojs/rss": "4.0.10",
|
"@astrojs/rss": "^4.0.12",
|
||||||
"@types/alpinejs": "^3.13.11",
|
"@types/alpinejs": "^3.13.11",
|
||||||
"@types/markdown-it": "^14.1.2",
|
"@types/markdown-it": "^14.1.2",
|
||||||
"alpinejs": "^3.14.8",
|
"alpinejs": "^3.14.8",
|
||||||
"astro": "^5.1.8",
|
"astro": "^5.13.3",
|
||||||
"astro-pagefind": "^1.7.0",
|
"astro-pagefind": "^1.7.0",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"markdown-it": "^14.1.0",
|
"markdown-it": "^14.1.0",
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 721 KiB After Width: | Height: | Size: 721 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 566 KiB After Width: | Height: | Size: 566 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 2.7 MiB After Width: | Height: | Size: 2.7 MiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 386 B After Width: | Height: | Size: 386 B |
|
Before Width: | Height: | Size: 619 B After Width: | Height: | Size: 619 B |
|
Before Width: | Height: | Size: 630 B After Width: | Height: | Size: 630 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 439 B After Width: | Height: | Size: 439 B |
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
import { Image } from 'astro:assets';
|
import { Image } from 'astro:assets';
|
||||||
|
|
||||||
import portrait from '../img/portrait.jpg';
|
import portrait from '../assets/portrait.jpg';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
size: number;
|
size: number;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
import { format, add } from 'date-fns';
|
import { format, add } from 'date-fns';
|
||||||
|
|
||||||
import CalendarIcon from '../img/svg/calendar.svg';
|
import CalendarIcon from '../assets/svg/calendar.svg';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -17,7 +17,7 @@ const { title, date, slug } = Astro.props;
|
|||||||
{slug ? <a href={`/blog/${slug}`}>{title}</a> : title}
|
{slug ? <a href={`/blog/${slug}`}>{title}</a> : title}
|
||||||
</h2>
|
</h2>
|
||||||
<div>
|
<div>
|
||||||
<CalendarIcon class="calendar-icon" size={24} />
|
<CalendarIcon class="calendar-icon" width={24} height={24} />
|
||||||
{format(add(new Date(date), { hours: 6 }), 'MMM do, y')}
|
{format(add(new Date(date), { hours: 6 }), 'MMM do, y')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
import RssIcon from '../img/svg/rss.svg';
|
import RssIcon from '../assets/svg/rss.svg';
|
||||||
const year = new Date().getFullYear();
|
const year = new Date().getFullYear();
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -13,12 +13,17 @@ const year = new Date().getFullYear();
|
|||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a href="/rss.xml"
|
<a href="/rss.xml"
|
||||||
><RssIcon class="rss-icon" size={20} /> Subscribe with RSS</a
|
><RssIcon class="rss-icon" width={20} height={20} /> Subscribe with RSS</a
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
footer {
|
||||||
|
margin: 4rem 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.rss-icon {
|
.rss-icon {
|
||||||
transform: translateY(0.18rem);
|
transform: translateY(0.18rem);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,15 +20,10 @@ const pathComponents = pathname.split('/').slice(1);
|
|||||||
---
|
---
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<div>
|
<a href="/">
|
||||||
{
|
<Avatar size={60} />
|
||||||
pathComponents[0] !== '' ? (
|
</a>
|
||||||
<a href="/" transition:name="my-avatar">
|
|
||||||
<Avatar size={60} />
|
|
||||||
</a>
|
|
||||||
) : null
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<nav>
|
<nav>
|
||||||
{
|
{
|
||||||
navLinks.map((link) => (
|
navLinks.map((link) => (
|
||||||
@@ -36,9 +31,7 @@ const pathComponents = pathname.split('/').slice(1);
|
|||||||
<a href={`/${link.path}`}>
|
<a href={`/${link.path}`}>
|
||||||
<span>{link.label}</span>
|
<span>{link.label}</span>
|
||||||
</a>
|
</a>
|
||||||
{pathComponents[0] === link.path ? (
|
{pathComponents[0] === link.path ? <div class="underline" /> : null}
|
||||||
<div class="underline" transition:name="menu-selection-indicator" />
|
|
||||||
) : null}
|
|
||||||
</li>
|
</li>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -46,6 +39,35 @@ const pathComponents = pathname.split('/').slice(1);
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header > h1 > a {
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
header > nav {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
list-style: none;
|
||||||
|
gap: 20px;
|
||||||
|
font-size: 1.15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
header > nav > li > a:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
header > nav > li > .selected {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
.underline {
|
.underline {
|
||||||
height: 2px;
|
height: 2px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const { post } = Astro.props;
|
|||||||
const { data, slug } = post;
|
const { data, slug } = post;
|
||||||
---
|
---
|
||||||
|
|
||||||
<article transition:name={slug}>
|
<article>
|
||||||
<BlogHeader title={post.data.title} date={data.pubDate} slug={slug} />
|
<BlogHeader title={post.data.title} date={data.pubDate} slug={slug} />
|
||||||
<Tags tags={data.tags} />
|
<Tags tags={data.tags} />
|
||||||
</article>
|
</article>
|
||||||
|
|||||||
@@ -9,9 +9,12 @@ const { tags } = Astro.props;
|
|||||||
<span>
|
<span>
|
||||||
{
|
{
|
||||||
tags.sort().map((tag: string, index: number) => (
|
tags.sort().map((tag: string, index: number) => (
|
||||||
<a class="tag" href={`/blog/archive/${tag}`}>
|
<>
|
||||||
{`${tag}${index < tags.length - 1 ? ', ' : ''}`}
|
<a class="tag" href={`/blog/archive/${tag}`}>
|
||||||
</a>
|
{tag}
|
||||||
|
</a>
|
||||||
|
{index < tags.length - 1 ? ' | ' : ''}
|
||||||
|
</>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ I decided to use [OpenAI](https://openai.com/) for the written portion, and [Eve
|
|||||||
|
|
||||||
I decided against using AI generated images because, even though I’m letting an AI create a dating profile for me, I want it to be somewhat genuine. Also I’m not looking to catfish anybody with this image I generated just to see how good AI could make me look:
|
I decided against using AI generated images because, even though I’m letting an AI create a dating profile for me, I want it to be somewhat genuine. Also I’m not looking to catfish anybody with this image I generated just to see how good AI could make me look:
|
||||||
|
|
||||||
<img src="/media/my-ai-portrait.jpg" width="260" alt="an AI-generated portrait of me" />
|

|
||||||
|
|
||||||
Originally this was going to be one post I’d write over the course of a week, but as I was writing I decided I should split it into 2 parts: Part 1, this part, about setting up the dating profile, and part 2, coming at the end of the experiment, about the experience and how it went.
|
Originally this was going to be one post I’d write over the course of a week, but as I was writing I decided I should split it into 2 parts: Part 1, this part, about setting up the dating profile, and part 2, coming at the end of the experiment, about the experience and how it went.
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ But I did find a rather salty post I wrote about a Mac theme I created back when
|
|||||||
|
|
||||||
If you were around the Mac customization scene–specifically on the now-defunct MacThemes.net–around 2007, you probably remember a little project called iLeopard. Admittedly, the chance that you’re in that extremely specific niché is incredibly small. The best data I could find is this [Ars Technica article](https://arstechnica.com/gadgets/2007/03/7296/) from March 2007 saying the Mac hit about 6% marketshare. And only a tiny subset of those users even had the thought that customizing the look of Mac OS X was something they wanted to do.
|
If you were around the Mac customization scene–specifically on the now-defunct MacThemes.net–around 2007, you probably remember a little project called iLeopard. Admittedly, the chance that you’re in that extremely specific niché is incredibly small. The best data I could find is this [Ars Technica article](https://arstechnica.com/gadgets/2007/03/7296/) from March 2007 saying the Mac hit about 6% marketshare. And only a tiny subset of those users even had the thought that customizing the look of Mac OS X was something they wanted to do.
|
||||||
|
|
||||||
<img src="/media/ileopard/mac-os-10-1.png" width="90%" alt="a screenshot of Mac OS X 10.1 with various apps open" />
|

|
||||||
|
|
||||||
*Mac OS X 10.1 and the Aqua Interface, from* [_GUIdebook_](https://guidebookgallery.org/screenshots/macosx101)
|
*Mac OS X 10.1 and the Aqua Interface, from* [_GUIdebook_](https://guidebookgallery.org/screenshots/macosx101)
|
||||||
|
|
||||||
I was one of the few that had that had that idea pop into my head. I was super into finding and downloading cool themes, including one I distinctly remember that looked like Windows Vista for some reason. It was 2007, the Aqua Interface (the playful, plastic-looking interface style Apple used for about a decade) was already feeling a little passé to me. I wanted something new, and weirdly enough that new thing came from Apple, in the form of iTunes 7.
|
I was one of the few that had that had that idea pop into my head. I was super into finding and downloading cool themes, including one I distinctly remember that looked like Windows Vista for some reason. It was 2007, the Aqua Interface (the playful, plastic-looking interface style Apple used for about a decade) was already feeling a little passé to me. I wanted something new, and weirdly enough that new thing came from Apple, in the form of iTunes 7.
|
||||||
|
|
||||||
<img src="/media/ileopard/itunes-7.gif" width="80%" alt="a screenshot iTunes 7 with the iTunes Music Store open" />
|

|
||||||
|
|
||||||
_iTunes 7 screenshot, from [AppleInsider](https://appleinsider.com/articles/06/09/12/apple_introduces_itunes_7_previews_itv_device)_
|
_iTunes 7 screenshot, from [AppleInsider](https://appleinsider.com/articles/06/09/12/apple_introduces_itunes_7_previews_itv_device)_
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ Being that I was a bored teenager in high school with way too much time on my ha
|
|||||||
|
|
||||||
After dozens of restarts, and a couple corrupted OS installs, I had figured out a good chunk of the building blocks making up Leopard’s UI. Even still, after weeks of this, there was still so much to be done. Bear in mind, there was no documentation on any of this stuff. I was about to create, as far as I know, the first theme for Mac OS X Leopard.
|
After dozens of restarts, and a couple corrupted OS installs, I had figured out a good chunk of the building blocks making up Leopard’s UI. Even still, after weeks of this, there was still so much to be done. Bear in mind, there was no documentation on any of this stuff. I was about to create, as far as I know, the first theme for Mac OS X Leopard.
|
||||||
|
|
||||||
<img src="/media/ileopard/ileopard-2-0-1.png" width="75%" alt="a screenshot of the Mac OS X Appearance preference pane showing off the modifications made by iLeopard" />
|

|
||||||
|
|
||||||
_iLeopard 2.0.1 screenshot, from [AmazingHenry on MacRumors](https://forums.macrumors.com/threads/ileopard-theme.2045553/), released by fellow MacThemes.net user ‘gcamp' after I essentially handed off the project_
|
_iLeopard 2.0.1 screenshot, from [AmazingHenry on MacRumors](https://forums.macrumors.com/threads/ileopard-theme.2045553/), released by fellow MacThemes.net user ‘gcamp' after I essentially handed off the project_
|
||||||
|
|
||||||
|
|||||||
@@ -12,17 +12,17 @@ I've never been a Gundam fan, the genre just never really appealed to me. But I
|
|||||||
|
|
||||||
So, there was a little anime I watched back in high school called [Full Metal Panic](https://en.wikipedia.org/wiki/Full_Metal_Panic!) which, like Gundam, features people piloting giant mecha. As luck would have it, Bandai, the makers of Gunpla kits, have/had the license to make kits for the series. Not-so-luckily, the kit I wanted based on the main character's mech is impossible to find at a reasonable price at the moment, so I settled for my second pick:
|
So, there was a little anime I watched back in high school called [Full Metal Panic](https://en.wikipedia.org/wiki/Full_Metal_Panic!) which, like Gundam, features people piloting giant mecha. As luck would have it, Bandai, the makers of Gunpla kits, have/had the license to make kits for the series. Not-so-luckily, the kit I wanted based on the main character's mech is impossible to find at a reasonable price at the moment, so I settled for my second pick:
|
||||||
|
|
||||||
<img src="/media/gunpla/box.jpg" width="90%" alt="a box sitting on a table with an image of a mech and text that reads 'M9 Gernsback ver IV (Agressor Squadron)'" />
|

|
||||||
|
|
||||||
So, with the kit in hand, I opened the box and was greeted by a handful of plastic pouches containing what seemed like a thousand parts connected on plastic sprues in a variety of blues and grays. I took out the contents and perused through the included instructions, which read very much like it was designed by Ikea, if Ikea sold miniature giant robots. The writing was all in Japanese, but the illustrations were enough to guide one through the process no matter what language they can read.
|
So, with the kit in hand, I opened the box and was greeted by a handful of plastic pouches containing what seemed like a thousand parts connected on plastic sprues in a variety of blues and grays. I took out the contents and perused through the included instructions, which read very much like it was designed by Ikea, if Ikea sold miniature giant robots. The writing was all in Japanese, but the illustrations were enough to guide one through the process no matter what language they can read.
|
||||||
|
|
||||||
<img src="/media/gunpla/all-the-parts.jpg" width="90%" alt="neatly piled plastic sprues sorted by color, an instruction booklet, and a model of R2-D2 lurking in the corner" />
|

|
||||||
|
|
||||||
Separating the individual pieces from each sprue was pretty painless. They came off very cleanly and I barely had to use my handy hobby knife to clean up bits of excess plastic, and when I did it cut through like butter. Not a drop of glue was needed, everything snapped together perfectly. I struggled a bit with the smaller parts (of which there were plenty), and there were a couple seemingly microscopic stickers I had to apply, so my iFixIt tweezers came in handy quite a few times.
|
Separating the individual pieces from each sprue was pretty painless. They came off very cleanly and I barely had to use my handy hobby knife to clean up bits of excess plastic, and when I did it cut through like butter. Not a drop of glue was needed, everything snapped together perfectly. I struggled a bit with the smaller parts (of which there were plenty), and there were a couple seemingly microscopic stickers I had to apply, so my iFixIt tweezers came in handy quite a few times.
|
||||||
|
|
||||||
After a couple hours, I had a pretty sweet looking miniature mecha.
|
After a couple hours, I had a pretty sweet looking miniature mecha.
|
||||||
|
|
||||||
<img src="/media/gunpla/final.jpg" width="90%" alt="the finished model, standing tall, and holding a scary looking gun" />
|

|
||||||
|
|
||||||
Putting it together, I was just so amazed by the level of engineering that went into this kit. Putting it all together, I could tell someone put a lot of care and attention into designing this thing, rivaling some of the Lego kits I've built, from the near-seamless fit of all the pieces, to the various types of joints enabling a quite frankly insane level of pose-ability for something this size.
|
Putting it together, I was just so amazed by the level of engineering that went into this kit. Putting it all together, I could tell someone put a lot of care and attention into designing this thing, rivaling some of the Lego kits I've built, from the near-seamless fit of all the pieces, to the various types of joints enabling a quite frankly insane level of pose-ability for something this size.
|
||||||
|
|
||||||
|
|||||||
@@ -32,11 +32,11 @@ Once both MUI Base and Tailwind are installed, we next need to run `yarn tailwin
|
|||||||
|
|
||||||
First thing's first, open up the project folder in your code editor of choice and at the root of the project you should see the 'tailwind.config.js' we created in step 2. Open that, and under `module.exports` you should see an empty array named 'content'. Add the following to the array: `'./src/**/*.{js,jsx,ts,tsx}'` Your config file should look like this:
|
First thing's first, open up the project folder in your code editor of choice and at the root of the project you should see the 'tailwind.config.js' we created in step 2. Open that, and under `module.exports` you should see an empty array named 'content'. Add the following to the array: `'./src/**/*.{js,jsx,ts,tsx}'` Your config file should look like this:
|
||||||
|
|
||||||
<img src="/media/mui-plus-tailwind/tailwind-config.png" width="80%" alt="module.exports = { content: ['./src/**/*.{js,jsx,ts,tsx}'], theme: { extend: {}, }, plugins: [],};">
|
<img src="src/assets/blog/mui-plus-tailwind/tailwind-config.png" width="80%" alt="module.exports = { content: ['./src/**/*.{js,jsx,ts,tsx}'], theme: { extend: {}, }, plugins: [],};">
|
||||||
|
|
||||||
Next, we need to import Tailwind into our 'globals.css' file, which is located in 'src/styles/'. We can delete everything in the file as we won't be needing any of it. Then, add the following:
|
Next, we need to import Tailwind into our 'globals.css' file, which is located in 'src/styles/'. We can delete everything in the file as we won't be needing any of it. Then, add the following:
|
||||||
|
|
||||||
<img src="/media/mui-plus-tailwind/global-css.png" width="80%" alt="@tailwind base; @tailwind components; @tailwind utilities;">
|
<img src="src/assets/blog/mui-plus-tailwind/global-css.png" width="80%" alt="@tailwind base; @tailwind components; @tailwind utilities;">
|
||||||
|
|
||||||
And that's it! We're ready to start creating something!
|
And that's it! We're ready to start creating something!
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ Ideally, we'd want to create smaller, reusable component files, but that's beyon
|
|||||||
|
|
||||||
First, we should delete everything we don't need so that our 'index.js' file looks like this:
|
First, we should delete everything we don't need so that our 'index.js' file looks like this:
|
||||||
|
|
||||||
<img src="/media/mui-plus-tailwind/index-js.png" width="80%" alt="export default function Home() { return() }">
|
<img src="src/assets/blog/mui-plus-tailwind/index-js.png" width="80%" alt="export default function Home() { return() }">
|
||||||
|
|
||||||
We're going to create a simple button, nothing too fancy. So first we need to import the MUI Unstyled Button component by adding `import { ButtonUnstyled as Button } from '@mui/base'` on the first line of our file.
|
We're going to create a simple button, nothing too fancy. So first we need to import the MUI Unstyled Button component by adding `import { ButtonUnstyled as Button } from '@mui/base'` on the first line of our file.
|
||||||
|
|
||||||
@@ -54,21 +54,21 @@ _Note: The official MUI docs say to import as `import ButtonUnstyled from '@mui/
|
|||||||
|
|
||||||
Now we can create our button. Inside the return statement, add the following:
|
Now we can create our button. Inside the return statement, add the following:
|
||||||
|
|
||||||
<img src="/media/mui-plus-tailwind/unstyled-button-code.png" width="80%" alt="<Button>Click Me!</Button>" />
|
<img src="src/assets/blog/mui-plus-tailwind/unstyled-button-code.png" width="80%" alt="<Button>Click Me!</Button>" />
|
||||||
|
|
||||||
Next, go back to your terminal and run `yarn dev` to start up the dev server, and click the 'localhost' link that appears (most likely 'localhost:3000'). We'll be greeted by what looks like just a bit of text (it's a button, I promise), which isn't what we want, but that's because we haven't added any styles yet!
|
Next, go back to your terminal and run `yarn dev` to start up the dev server, and click the 'localhost' link that appears (most likely 'localhost:3000'). We'll be greeted by what looks like just a bit of text (it's a button, I promise), which isn't what we want, but that's because we haven't added any styles yet!
|
||||||
|
|
||||||
<img src="/media/mui-plus-tailwind/unstyled-button.png" width="40%" alt="A plain old 'button' that reads 'Click Me!'" />
|
<img src="src/assets/blog/mui-plus-tailwind/unstyled-button.png" width="40%" alt="A plain old 'button' that reads 'Click Me!'" />
|
||||||
|
|
||||||
All we need to do is add the `className` prop with some nice Tailwind utility classes:
|
All we need to do is add the `className` prop with some nice Tailwind utility classes:
|
||||||
|
|
||||||
<img src="/media/mui-plus-tailwind/styled-button-code.png" width="80%" alt="<Button className='rounded-lg border-2 border-sky-500 bg-sky-600 py-2 px-10 font-medium text-slate-300 shadow hover:bg-sky-700 active:translate-y-0.5'>
|
<img src="src/assets/blog/mui-plus-tailwind/styled-button-code.png" width="80%" alt="<Button className='rounded-lg border-2 border-sky-500 bg-sky-600 py-2 px-10 font-medium text-slate-300 shadow hover:bg-sky-700 active:translate-y-0.5'>
|
||||||
Click Me!
|
Click Me!
|
||||||
</Button>" />
|
</Button>" />
|
||||||
|
|
||||||
Now, when we go back to the browser, we should see the following:
|
Now, when we go back to the browser, we should see the following:
|
||||||
|
|
||||||
<img src="/media/mui-plus-tailwind/styled-button.png" width="40%" alt="A styled blue button that reads 'Click Me!'" />
|
<img src="src/assets/blog/mui-plus-tailwind/styled-button.png" width="40%" alt="A styled blue button that reads 'Click Me!'" />
|
||||||
|
|
||||||
Nice! That looks a lot more like a button.
|
Nice! That looks a lot more like a button.
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: Manrope;
|
font-family: Maple Mono;
|
||||||
src: url(./fonts/Manrope-Regular.ttf) format('truetype');
|
src: url(./assets/fonts/MapleMono-Regular.ttf) format('truetype');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: Manrope;
|
font-family: Maple Mono;
|
||||||
src: url(./fonts/Manrope-Bold.ttf) format('truetype');
|
src: url(./assets/fonts/MapleMono-Bold.ttf) format('truetype');
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: JetBrainsMono;
|
font-family: JetBrainsMono;
|
||||||
src: url(./fonts/JetBrainsMono-VariableFont_wght.ttf) format('truetype');
|
src: url(./assets/fonts/JetBrainsMono-VariableFont_wght.ttf)
|
||||||
|
format('truetype');
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
}
|
}
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
--red: #d20f39;
|
--red: #d20f39;
|
||||||
--orange: #fe640b;
|
--orange: #fe640b;
|
||||||
--text: #4c4f69;
|
--text: #4c4f69;
|
||||||
--background: white;
|
--background: #fff8fa;
|
||||||
--radius: 5px;
|
--radius: 5px;
|
||||||
--shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px;
|
--shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px;
|
||||||
--border: 1px solid #8c8fa1;
|
--border: 1px solid #8c8fa1;
|
||||||
@@ -46,7 +47,7 @@
|
|||||||
html {
|
html {
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
background-color: var(--background);
|
background-color: var(--background);
|
||||||
font-family: 'Manrope', sans-serif;
|
font-family: 'Maple Mono', monospace;
|
||||||
font-size: 1.05rem;
|
font-size: 1.05rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,38 +66,9 @@ a:hover {
|
|||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
main p {
|
||||||
display: flex;
|
text-align: justify;
|
||||||
flex-direction: row;
|
line-height: 1.4;
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
height: 120px;
|
|
||||||
}
|
|
||||||
|
|
||||||
header > h1 > a {
|
|
||||||
color: var(--text);
|
|
||||||
}
|
|
||||||
|
|
||||||
header > nav {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
list-style: none;
|
|
||||||
gap: 20px;
|
|
||||||
font-size: 1.15rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
header > nav > li > a:hover {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
header > nav > li > .selected {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
margin: 4rem 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
article img {
|
article img {
|
||||||
@@ -104,6 +76,8 @@ article img {
|
|||||||
box-shadow: var(--shadow);
|
box-shadow: var(--shadow);
|
||||||
display: block;
|
display: block;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-shadow {
|
.no-shadow {
|
||||||
@@ -124,12 +98,6 @@ article img {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.more-posts {
|
|
||||||
text-align: center;
|
|
||||||
padding: 1rem;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
blockquote {
|
||||||
border-left: 4px solid;
|
border-left: 4px solid;
|
||||||
border-color: var(--text);
|
border-color: var(--text);
|
||||||
@@ -159,24 +127,6 @@ kbd {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Not By AI badge */
|
|
||||||
|
|
||||||
.not-by-ai {
|
|
||||||
display: block;
|
|
||||||
width: 134px;
|
|
||||||
height: 45px;
|
|
||||||
background-image: url(svg/Written-By-Human-Not-By-AI-Badge-white.svg);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center;
|
|
||||||
margin: 1rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
.not-by-ai {
|
|
||||||
background-image: url(svg/Written-By-Human-Not-By-AI-Badge-black.svg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 590px) {
|
@media (max-width: 590px) {
|
||||||
header {
|
header {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
---
|
---
|
||||||
import '@styles/global.css';
|
import '../global.css';
|
||||||
import { ClientRouter } from 'astro:transitions';
|
|
||||||
|
|
||||||
import Header from '@components/Header.astro';
|
import Header from '@components/Header.astro';
|
||||||
import Footer from '@components/Footer.astro';
|
import Footer from '@components/Footer.astro';
|
||||||
@@ -23,16 +22,15 @@ const title = Astro.props.title || Astro.props.frontmatter?.title || 'Unknown';
|
|||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<meta name="robots" content="noindex" /><meta
|
<meta name="robots" content="noindex" /><meta
|
||||||
name="description"
|
name="description"
|
||||||
content="My personal blog about life, gaming, tech, and whatever else I feel like writing about."
|
content="My little space on the World Wide Web."
|
||||||
/><link
|
/><link
|
||||||
rel="alternate"
|
rel="alternate"
|
||||||
type="application/rss+xml"
|
type="application/rss+xml"
|
||||||
title="ghall.space - RSS"
|
title="ghall.space - RSS"
|
||||||
href={`${Astro.site}rss.xml`}
|
href={`${Astro.site}rss.xml`}
|
||||||
/><title>{`ghall.space - ${title}`}</title>
|
/><title>{`ghall.space - ${title}`}</title>
|
||||||
<ClientRouter />
|
|
||||||
</head>
|
</head>
|
||||||
<body class="layout-simple" transition:animate="fade">
|
<body class="layout-simple">
|
||||||
<Header />
|
<Header />
|
||||||
<main>
|
<main>
|
||||||
<slot />
|
<slot />
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import Layout from '@layouts/Layout.astro';
|
import Layout from '@layouts/Layout.astro';
|
||||||
import { Image } from 'astro:assets';
|
import { Image } from 'astro:assets';
|
||||||
|
|
||||||
import DataGif from '../img/it-does-not-exist.gif';
|
import DataGif from '../assets/it-does-not-exist.gif';
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout title="Not Found">
|
<Layout title="Not Found">
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ export async function getStaticPaths() {
|
|||||||
|
|
||||||
const { post } = Astro.props;
|
const { post } = Astro.props;
|
||||||
|
|
||||||
const { data, slug } = post;
|
const { data } = post;
|
||||||
|
|
||||||
const { Content } = await post.render();
|
const { Content } = await post.render();
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout title={data.title}>
|
<Layout title={data.title}>
|
||||||
<article transition:name={slug}>
|
<article>
|
||||||
<BlogHeader title={data.title} date={data.pubDate} />
|
<BlogHeader title={data.title} date={data.pubDate} />
|
||||||
<Content />
|
<Content />
|
||||||
<a href="https://notbyai.fyi/">
|
<a href="https://notbyai.fyi/">
|
||||||
@@ -30,3 +30,21 @@ const { Content } = await post.render();
|
|||||||
<Tags tags={data.tags} />
|
<Tags tags={data.tags} />
|
||||||
</article>
|
</article>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.not-by-ai {
|
||||||
|
display: block;
|
||||||
|
width: 134px;
|
||||||
|
height: 45px;
|
||||||
|
background-image: url(../../assets/svg/Written-By-Human-Not-By-AI-Badge-white.svg);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.not-by-ai {
|
||||||
|
background-image: url(../../assets/svg/Written-By-Human-Not-By-AI-Badge-black.svg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ posts.sort(
|
|||||||
<ul>
|
<ul>
|
||||||
{
|
{
|
||||||
posts.map(({ slug, data }) => (
|
posts.map(({ slug, data }) => (
|
||||||
<li transition:name={slug}>
|
<li>
|
||||||
<a href={`/posts/${slug}`}>{data.title}</a> -
|
<a href={`/posts/${slug}`}>{data.title}</a> -
|
||||||
<span>
|
<span>
|
||||||
{format(add(new Date(data.pubDate), { hours: 6 }), 'MMM do, y')}
|
{format(add(new Date(data.pubDate), { hours: 6 }), 'MMM do, y')}
|
||||||
|
|||||||
@@ -26,3 +26,11 @@ const posts = await getCollection('blog');
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.more-posts {
|
||||||
|
text-align: center;
|
||||||
|
padding: 1rem;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import { getCollection } from 'astro:content';
|
|||||||
import Layout from '@layouts/Layout.astro';
|
import Layout from '@layouts/Layout.astro';
|
||||||
import Avatar from '@components/Avatar.astro';
|
import Avatar from '@components/Avatar.astro';
|
||||||
|
|
||||||
import MastodonIcon from '../img/svg/mastodon.svg';
|
import MastodonIcon from '../assets/svg/mastodon.svg';
|
||||||
import BlueskyIcon from '../img/svg/bluesky.svg';
|
import BlueskyIcon from '../assets/svg/bluesky.svg';
|
||||||
|
|
||||||
const iconSize = 16;
|
const iconSize = 16;
|
||||||
const posts = await getCollection('blog');
|
const posts = await getCollection('blog');
|
||||||
@@ -19,9 +19,6 @@ const latestPost = posts.sort(
|
|||||||
---
|
---
|
||||||
|
|
||||||
<Layout title="Welcome">
|
<Layout title="Welcome">
|
||||||
<div transition:name="my-avatar">
|
|
||||||
<Avatar size={200} />
|
|
||||||
</div>
|
|
||||||
<p>
|
<p>
|
||||||
My name is <strong>Graham</strong> (he/him), a full-stack web developer, and
|
My name is <strong>Graham</strong> (he/him), a full-stack web developer, and
|
||||||
tech enthusiast.
|
tech enthusiast.
|
||||||
@@ -50,10 +47,12 @@ const latestPost = posts.sort(
|
|||||||
If you want to get in touch, I'm on <a
|
If you want to get in touch, I'm on <a
|
||||||
rel="me"
|
rel="me"
|
||||||
href="https://mastodon.social/@ghalldev"
|
href="https://mastodon.social/@ghalldev"
|
||||||
target="_blank"><MastodonIcon size={iconSize} />Mastodon</a
|
target="_blank"
|
||||||
|
><MastodonIcon width={iconSize} height={iconSize} />Mastodon</a
|
||||||
> and <a
|
> and <a
|
||||||
href="https://bsky.app/profile/ghalldev.bsky.social"
|
href="https://bsky.app/profile/ghalldev.bsky.social"
|
||||||
target="_blank"><BlueskyIcon size={iconSize} />Bluesky</a
|
target="_blank"
|
||||||
|
><BlueskyIcon width={iconSize} height={iconSize} />Bluesky</a
|
||||||
>.
|
>.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@@ -5,19 +5,17 @@ title: Now
|
|||||||
|
|
||||||
Hey there, this is my [/now page](https://nownownow.com/about)!
|
Hey there, this is my [/now page](https://nownownow.com/about)!
|
||||||
|
|
||||||
_Last updated: July 28, 2025_
|
_Last updated: August 29, 2025_
|
||||||
|
|
||||||
## 🎧 Listening
|
## 🎧 Listening
|
||||||
|
|
||||||
- [Tunnel Vision - Beach Bunny](https://album.link/i/1794412465)
|
- [Ego Death at a Bachelorette Party - Hayley Williams](https://album.link/i/1833006180)
|
||||||
- [Flood - They Might Be Giants](https://album.link/us/i/298111036)
|
|
||||||
- [Video Game Legends - Celestial Aeon Project, Deck Hard & Stellar Conflux](https://album.link/i/1814286119)
|
|
||||||
|
|
||||||
## 🎮 Playing
|
## 🎮 Playing
|
||||||
|
|
||||||
|
- [Batman Arkham Asylum](https://www.igdb.com/games/batman-arkham-asylum)
|
||||||
- [Assassin's Creed Shadows](https://www.igdb.com/games/assassins-creed-shadows)
|
- [Assassin's Creed Shadows](https://www.igdb.com/games/assassins-creed-shadows)
|
||||||
- [Baldur's Gate 3](https://www.igdb.com/games/baldurs-gate-3)
|
- [Baldur's Gate 3](https://www.igdb.com/games/baldurs-gate-3)
|
||||||
- [Rune Factory: Guardians of Azuma](https://www.igdb.com/games/rune-factory-guardians-of-azuma--1)
|
|
||||||
|
|
||||||
## 📺 Watching
|
## 📺 Watching
|
||||||
|
|
||||||
|
|||||||