the chunkiest update
@ -1,6 +1,6 @@
|
||||
// @ts-check
|
||||
import { defineConfig } from 'astro/config';
|
||||
import { modifiedTime } from './src/utils/last-modified.mjs';
|
||||
import { modifiedTime } from './src/utils/lastModified.mjs';
|
||||
import alpinejs from '@astrojs/alpinejs';
|
||||
|
||||
// https://astro.build/config
|
||||
@ -8,6 +8,7 @@ export default defineConfig({
|
||||
site: "https://haetae.32-b.it",
|
||||
markdown: {
|
||||
remarkPlugins: [modifiedTime],
|
||||
smartypants: false,
|
||||
},
|
||||
integrations: [alpinejs()],
|
||||
});
|
13
bun.lock
@ -12,6 +12,7 @@
|
||||
"astro-breadcrumbs": "^3.3.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"markdown-it": "^14.1.0",
|
||||
"node-html-parser": "^7.0.1",
|
||||
"sanitize-html": "^2.15.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -252,6 +253,8 @@
|
||||
|
||||
"base-64": ["base-64@1.0.0", "", {}, "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg=="],
|
||||
|
||||
"boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
|
||||
|
||||
"boxen": ["boxen@8.0.1", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^8.0.0", "chalk": "^5.3.0", "cli-boxes": "^3.0.0", "string-width": "^7.2.0", "type-fest": "^4.21.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0" } }, "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw=="],
|
||||
|
||||
"camelcase": ["camelcase@8.0.0", "", {}, "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA=="],
|
||||
@ -292,6 +295,10 @@
|
||||
|
||||
"crossws": ["crossws@0.3.4", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-uj0O1ETYX1Bh6uSgktfPvwDiPYGQ3aI4qVsaC/LWpkIzGj1nUYm5FK3K+t11oOlpN01lGbprFCH4wBlKdJjVgw=="],
|
||||
|
||||
"css-select": ["css-select@5.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg=="],
|
||||
|
||||
"css-what": ["css-what@6.1.0", "", {}, "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw=="],
|
||||
|
||||
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
|
||||
|
||||
"dayjs": ["dayjs@1.11.13", "", {}, "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="],
|
||||
@ -382,6 +389,8 @@
|
||||
|
||||
"hastscript": ["hastscript@9.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^6.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-jzaLBGavEDKHrc5EfFImKN7nZKKBdSLIdGvCwDZ9TfzbF2ffXiov8CKE445L2Z1Ek2t/m4SKQ2j6Ipv7NyUolw=="],
|
||||
|
||||
"he": ["he@1.2.0", "", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="],
|
||||
|
||||
"html-escaper": ["html-escaper@3.0.3", "", {}, "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="],
|
||||
|
||||
"html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
|
||||
@ -522,10 +531,14 @@
|
||||
|
||||
"node-fetch-native": ["node-fetch-native@1.6.6", "", {}, "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ=="],
|
||||
|
||||
"node-html-parser": ["node-html-parser@7.0.1", "", { "dependencies": { "css-select": "^5.1.0", "he": "1.2.0" } }, "sha512-KGtmPY2kS0thCWGK0VuPyOS+pBKhhe8gXztzA2ilAOhbUbxa9homF1bOyKvhGzMLXUoRds9IOmr/v5lr/lqNmA=="],
|
||||
|
||||
"node-mock-http": ["node-mock-http@1.0.0", "", {}, "sha512-0uGYQ1WQL1M5kKvGRXWQ3uZCHtLTO8hln3oBjIusM75WoesZ909uQJs/Hb946i2SS+Gsrhkaa6iAO17jRIv6DQ=="],
|
||||
|
||||
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
|
||||
|
||||
"nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
|
||||
|
||||
"ofetch": ["ofetch@1.4.1", "", { "dependencies": { "destr": "^2.0.3", "node-fetch-native": "^1.6.4", "ufo": "^1.5.4" } }, "sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw=="],
|
||||
|
||||
"ohash": ["ohash@1.1.4", "", {}, "sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g=="],
|
||||
|
@ -17,6 +17,7 @@
|
||||
"astro-breadcrumbs": "^3.3.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"markdown-it": "^14.1.0",
|
||||
"node-html-parser": "^7.0.1",
|
||||
"sanitize-html": "^2.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
Before Width: | Height: | Size: 411 B After Width: | Height: | Size: 411 B |
Before Width: | Height: | Size: 689 B After Width: | Height: | Size: 689 B |
BIN
src/assets/gallery/cian.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
src/assets/gallery/idle_pngtuber.png
Normal file
After Width: | Height: | Size: 171 KiB |
BIN
src/assets/gallery/masaki.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
src/assets/gallery/sample1.png
Normal file
After Width: | Height: | Size: 303 KiB |
BIN
src/assets/gallery/sample2.png
Normal file
After Width: | Height: | Size: 777 KiB |
BIN
src/assets/gallery/sample3.png
Normal file
After Width: | Height: | Size: 683 KiB |
BIN
src/assets/gallery/thumbnail.png
Normal file
After Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 169 B After Width: | Height: | Size: 169 B |
Before Width: | Height: | Size: 731 B After Width: | Height: | Size: 731 B |
Before Width: | Height: | Size: 699 B After Width: | Height: | Size: 699 B |
BIN
src/assets/images/the-of-all-time.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 254 B After Width: | Height: | Size: 254 B |
Before Width: | Height: | Size: 252 B After Width: | Height: | Size: 252 B |
Before Width: | Height: | Size: 252 B After Width: | Height: | Size: 252 B |
Before Width: | Height: | Size: 255 B After Width: | Height: | Size: 255 B |
Before Width: | Height: | Size: 255 B After Width: | Height: | Size: 255 B |
Before Width: | Height: | Size: 255 B After Width: | Height: | Size: 255 B |
Before Width: | Height: | Size: 302 B After Width: | Height: | Size: 302 B |
@ -5,14 +5,14 @@ interface Props {
|
||||
imagePath: string;
|
||||
alt: string;
|
||||
caption?: string;
|
||||
className?: string[] | string;
|
||||
class?: string | string[];
|
||||
}
|
||||
|
||||
const { imagePath, alt, caption, className }: Props = Astro.props;
|
||||
const { imagePath, alt, caption, class: className }: Props = Astro.props;
|
||||
const images = import.meta.glob<{ default: ImageMetadata }>("/src/assets/**/*.{jpeg,jpg,png,webp,gif}");
|
||||
if (!images[imagePath]) throw new Error(`"${imagePath}" does not exist in glob: "src/assets/**/*.{jpeg,jpg,png,webp,gif}"`);
|
||||
---
|
||||
<figure class:list={className}>
|
||||
<figure class:list={[className]}>
|
||||
<Image src={images[imagePath]()} {alt} />
|
||||
{caption && <figcaption>{caption}</figcaption>}
|
||||
</figure>
|
8
src/content/blog/birthday.md
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
title: birthday
|
||||
pubDate: 2024-05-01
|
||||
---
|
||||
|
||||
it's my birthday today! i'm turning older two times each year because i technically have two ages... it's becoming harder to come to terms with this the older i get. orz
|
||||
|
||||
oh well! at least cake is eternal.
|
10
src/content/blog/first-post.md
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
title: first post!
|
||||
pubDate: 2024-04-29
|
||||
---
|
||||
|
||||
i've remade my personal website again, this time with the sole purpose of creating profiles of my rp characters and maybe yap about my interests more.
|
||||
|
||||
i think my time on the internet has made me intensely private and introverted, so it's a little hard for me to really put myself out there both irl and digitally. there's just a lot that feels scary about the world, but i'd never be able to make any connections with people if i let my fears hold me back.
|
||||
|
||||
this is my attempt to share a part of myself, however small and trivial it may be.
|
@ -1,19 +0,0 @@
|
||||
---
|
||||
title: hey girl hey
|
||||
pubDate: 2024-02-03
|
||||
currently:
|
||||
mood: happy
|
||||
reading: hella
|
||||
watching: stuff
|
||||
playing: balatro
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin pretium augue elit, eget interdum massa lobortis ut. Praesent facilisis ornare aliquam. Donec sit amet volutpat ipsum, id ultricies urna. Donec vestibulum sagittis felis, tempor fermentum urna posuere at. Vestibulum cursus mauris eget bibendum blandit. Aenean at augue porttitor, bibendum massa ut, laoreet lacus. Phasellus fermentum tincidunt lectus vel volutpat. Proin sagittis vel sem sit amet consequat. Vestibulum ac laoreet quam. Mauris eu purus sit amet odio maximus dictum. Mauris quam tellus, tempus eu faucibus in, mollis quis velit. Phasellus nisl mauris, congue vel magna a, rutrum aliquet ante. Proin in ante pharetra, vestibulum nisi vel, fringilla tortor. Etiam mattis, mauris et mattis sagittis, orci eros ornare risus, vitae consequat ligula nulla eget quam.
|
||||
|
||||
Fusce malesuada sed risus eget elementum. Quisque porttitor finibus libero, et semper magna pretium ac. Donec gravida erat iaculis ante cursus, in laoreet ligula dapibus. Vestibulum gravida lorem eleifend mollis hendrerit. Nulla et velit dapibus, aliquet lectus at, mattis nulla. Pellentesque lacus eros, sagittis quis dignissim a, malesuada ut eros. Aliquam fringilla a leo sed commodo. Sed eu ligula malesuada, ultrices orci eget, suscipit ipsum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras interdum libero sed leo eleifend tempor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
|
||||
|
||||
Vivamus scelerisque ac quam sed interdum. Mauris pulvinar aliquet est in luctus. Donec convallis dui aliquam urna pellentesque, eget maximus tellus scelerisque. Integer posuere commodo justo in finibus. Vivamus at molestie nisl, sit amet ultrices ex. Ut elementum dignissim dui a lacinia. Etiam nec purus ac felis congue tristique ut eget leo. Aenean id augue molestie, auctor quam ac, aliquam magna. Maecenas condimentum sem mauris, in sodales nunc suscipit non. Sed iaculis ut magna vel sollicitudin. Etiam commodo lacinia lorem, quis tincidunt sem laoreet et. Sed interdum elit ac erat blandit cursus. Pellentesque imperdiet placerat lacus id sagittis. In vitae efficitur ante, sit amet feugiat nunc. Praesent erat mi, hendrerit molestie maximus sed, tempus eu massa.
|
||||
|
||||
Fusce convallis ultricies orci, vulputate laoreet magna. Proin in aliquet diam. Etiam placerat ante eget lacus ultrices fermentum. Phasellus interdum facilisis ex mattis blandit. Quisque vitae convallis velit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla sodales tincidunt lorem. Ut bibendum posuere elementum. Phasellus arcu lacus, porta sit amet convallis a, finibus et nisi. Sed id justo dapibus, faucibus elit eu, tincidunt ante.
|
||||
|
||||
Donec vehicula ultrices egestas. Maecenas non magna tortor. Curabitur metus sapien, ultricies porta urna vitae, pulvinar blandit tellus. In aliquet risus sed libero vulputate, in volutpat ligula malesuada. Phasellus viverra pretium turpis, quis congue dolor fringilla vitae. Morbi orci lacus, mollis non dolor nec, euismod aliquet risus. Ut massa nibh, maximus sit amet turpis et, dapibus ullamcorper turpis. Aenean ut tellus ac nisi mollis tempus. Duis at libero quis felis ornare viverra ac sit amet justo. Sed gravida magna ut nibh tincidunt, consectetur lobortis est consequat. Phasellus ex velit, tincidunt sit amet consectetur id, vulputate eget tellus. Donec ligula elit, vestibulum non tincidunt eu, ullamcorper eget massa. Nulla venenatis maximus metus, sed pulvinar dolor ullamcorper at. Vestibulum volutpat tortor ante, a ornare justo porttitor tempus. Aliquam sit amet sem porta, hendrerit dolor sed, tristique neque.
|
@ -1,9 +0,0 @@
|
||||
---
|
||||
title: hmhmhm!
|
||||
pubDate: 2024-02-04
|
||||
currently:
|
||||
mood: sad
|
||||
reading: sad fanfic
|
||||
---
|
||||
|
||||
hey there this is a test
|
16
src/content/blog/minor-updates.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
title: minor updates
|
||||
pubDate: 2024-12-12
|
||||
---
|
||||
|
||||
it's been a while since i last touched this website and 11ty went thru some updates and i thought i could also do some renovations on my end too. i had a lot of trouble trying to redo the guestbook to make it cleaner in terms of scripting. it gets real mad if i try to introduce a new script file and i can't make it play nice with 11ty filters, so i'll just have to copy-paste some stuff to make it happy.
|
||||
|
||||
though what reminded me of this website and why i wanted to update again was because of this hilarious mssg i got (shout out to morpho)
|
||||
|
||||

|
||||
|
||||
i wanted to save this before i cleaned up my guestbook entries bcs i thought it was the funniest thing i've seen all month. ilu so much, i hope ur having a wonderful day LMAO
|
||||
|
||||
i've been back at the grind with webdev and coding stuff, making little games where i can. i also tried to make a pomodoro game, but fell short right as i got all the mechanics working bcs i couldn't make a functioning save/load system in godot without it crashing on me. it definitely taught me not to overlook an important feature from the beginning.
|
||||
|
||||
right now i'm working on a mini turn-based rpg, and hopefully it'll be less groan-inducing than the last project.
|
10
src/content/blog/nuzlocke.md
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
title: nuzlocke
|
||||
pubDate: 2024-05-24
|
||||
---
|
||||
|
||||
i'm thinking about starting a nuzlocke run in the gs chronicle hack. i've always had a fond spot for johto ever since soulsilver came out - i remember the pokewalker and suicune encounter vividly. i just wish i was in a place to also get all the event pokemon, but i missed out on a lot of them.
|
||||
|
||||
in any case, i think gs chronicle would be a good start because one, it's a hack, two, it's johto, and three, i can (probably) make a little story out of it. apparently in the hack the story is slightly altered from soulsilver's and crystal's original, so that'll be fun to play around with.
|
||||
|
||||
i'm also toying around with the idea of starting a nuzlocke in pokemon platinum, since i've never played that game before and my second and most memorable pokemon game i've ever had was pokemon pearl. hmm, decisions decisions.
|
14
src/content/blog/t-log.md
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
title: t log number ??1
|
||||
pubDate: 2024-05-19
|
||||
---
|
||||
|
||||
it's been about two - now almost three - years since i started testosterone. this past week, i went to go get my hormone levels checked, but without getting into a lot of details i hashed out some issues with my doctor and now *hopefully* everything should be sorted out. i just hate miscommunication between my doctor, insurance, and pharmacy. the fact that insurance is essentially a middleman makes it unnecessarily 10x more difficult for me to get meds.
|
||||
|
||||
i found it hilarious that the nurse who took my blood sample also played stardew valley and final fantasy xiv, and fps games otherwise. she told me she mained ninja in ffxiv at the moment and didn't like modding in stardew valley. which fair! updating mods and getting them all organized without a good mod manager is a goddamn hassle lmao.
|
||||
|
||||
tips for any trans dudes thinking about transitioning with hormones: note that injections are (usually) cheaper and more effective. you can also increase dosage, but it has to be slowly with your doctor's supervision and advice. you can't increase dosage while on gels, you just take them as is. this next tidbit is more US-oriented, but i've found that my state's medicaid covers gels for free. check your state and insurance to see if they cover them, because they're expensive otherwise.
|
||||
|
||||
please feel free to ~~send me an email at xhaetae@proton.me~~ reach out to me on the [32-bit cafe discourse forum](https://discourse.32bit.cafe/u/haetae) if you have any questions. i'm not a doctor, but i've had some experience navigating transitioning and the systems surrounding the process.
|
||||
|
||||
(edit: unfortunately [proton is sketchy](https://news.ycombinator.com/item?id=42837181), so that email's nuked)
|
@ -4,8 +4,8 @@ import Layout from "./Layout.astro";
|
||||
import Navbar from "~/Navbar.astro";
|
||||
import Figure from "~/Figure.astro";
|
||||
|
||||
import border from "$/images/border.png";
|
||||
import frame from "$/images/frame.png";
|
||||
import border from "$/border.png";
|
||||
import frame from "$/frame.png";
|
||||
|
||||
type Props = MarkdownLayoutProps<{
|
||||
avatar?: string;
|
||||
@ -19,7 +19,7 @@ const { frontmatter } = Astro.props;
|
||||
|
||||
<main>
|
||||
{frontmatter.avatar && frontmatter.avatarText && (
|
||||
<Figure className="avatar" imagePath={frontmatter.avatar} alt={frontmatter.avatarText} />
|
||||
<Figure class="avatar" imagePath={frontmatter.avatar} alt={frontmatter.avatarText} />
|
||||
)}
|
||||
<article>
|
||||
<slot />
|
||||
|
@ -1,15 +1,14 @@
|
||||
---
|
||||
import { Image } from "astro:assets";
|
||||
import { getCollection } from "astro:content";
|
||||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import Layout from "./Layout.astro";
|
||||
import Navbar from "~/Navbar.astro";
|
||||
import moods from "@/utils/moods";
|
||||
|
||||
import outerBBS from "$/images/guild-bbs.png";
|
||||
import innerBBS from "$/images/guild-bbs-content.png";
|
||||
import sideBBS from "$/images/guild-bbs-list.png";
|
||||
import outerBBS from "$/guild-bbs.png";
|
||||
import innerBBS from "$/guild-bbs-content.png";
|
||||
import sideBBS from "$/guild-bbs-list.png";
|
||||
import formatDate from "@/utils/formatDate";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
@ -26,11 +25,11 @@ interface Props {
|
||||
const { title, date, currently } = Astro.props;
|
||||
|
||||
const blog = await getCollection("blog");
|
||||
blog.length = Math.min(blog.length, 5);
|
||||
blog.length = Math.min(blog.length, 12);
|
||||
blog.sort((a, b) => a.data.pubDate!.valueOf() - b.data.pubDate!.valueOf());
|
||||
dayjs.extend(utc);
|
||||
---
|
||||
<Layout {title}>
|
||||
<slot slot="head" name="head" />
|
||||
<Navbar />
|
||||
|
||||
<main>
|
||||
@ -42,9 +41,12 @@ dayjs.extend(utc);
|
||||
{blog.map(entry => (
|
||||
<li>
|
||||
<span class="item">
|
||||
<a href={`/blog/${entry.id}`}>{entry.data.title}</a>
|
||||
<time datetime={dayjs(entry.data.pubDate!).utc(true).toISOString()}>
|
||||
{dayjs(entry.data.pubDate).utc(true).format("M/D/YY")}
|
||||
<a href={`/blog/${entry.id}`} class="entry-title" aria-labelledby={entry.id}>
|
||||
<span style="display: none">{entry.data.title}</span>
|
||||
</a>
|
||||
<span id={entry.id} class="link" aria-hidden="true">{entry.data.title}</span>
|
||||
<time datetime={formatDate(entry.data.pubDate!, true)}>
|
||||
{formatDate(entry.data.pubDate!, false, "MM/DD/YY")}
|
||||
</time>
|
||||
</span>
|
||||
</li>
|
||||
@ -54,8 +56,8 @@ dayjs.extend(utc);
|
||||
<div class="nav-section">
|
||||
<h1><span class="title">other links</span></h1>
|
||||
<ul>
|
||||
<li><span class="item"><a href="/blog">archive</a></span></li>
|
||||
<li><span class="item"><a href="/blog/rss.xml">rss feed</a></span></li>
|
||||
<li><span class="item"><a href="/blog" class="link">archive</a></span></li>
|
||||
<li><span class="item"><a href="/blog/rss.xml" class="link">rss feed</a></span></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@ -67,9 +69,9 @@ dayjs.extend(utc);
|
||||
<h1>{title}</h1>
|
||||
<hr />
|
||||
<div class="info">
|
||||
<time datetime={dayjs(date).utc(true).toISOString()}>
|
||||
<time datetime={formatDate(date, true)}>
|
||||
<span class="title">Date</span>
|
||||
<span class="desc">{dayjs(date).utc(true).format("MMMM DD, YYYY")}</span>
|
||||
<span class="desc">{formatDate(date)}</span>
|
||||
</time>
|
||||
</div>
|
||||
</header>
|
||||
@ -208,13 +210,25 @@ dayjs.extend(utc);
|
||||
border-bottom: 2px solid var(--bg-4);
|
||||
|
||||
.item {
|
||||
position: relative;
|
||||
font-family: var(--mplus-10-font);
|
||||
font-size: 1.254rem;
|
||||
font-size: 1.25rem; /* this should be 20px */
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
a {
|
||||
&.entry-title {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.link {
|
||||
flex: 1;
|
||||
background-color: var(--bg-1);
|
||||
padding: 6px;
|
||||
@ -228,7 +242,7 @@ dayjs.extend(utc);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
a { background-color: var(--bg-7); }
|
||||
.link { background-color: var(--bg-7); }
|
||||
time { background-color: var(--border-6); }
|
||||
}
|
||||
}
|
||||
@ -254,6 +268,7 @@ dayjs.extend(utc);
|
||||
}
|
||||
|
||||
article {
|
||||
display: flex;
|
||||
letter-spacing: 1px;
|
||||
border: 2px solid var(--border-0);
|
||||
border-image: var(--outerBorder) 19 9 38 11 fill / 38px 18px 76px 22px;
|
||||
@ -261,6 +276,8 @@ dayjs.extend(utc);
|
||||
padding: 36px 16px 74px 20px;
|
||||
|
||||
.inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 2px solid var(--border-2);
|
||||
border-image: var(--innerBorder) 3 2 2 fill / 6px 4px 4px;
|
||||
margin: 14px 4px 8px;
|
||||
@ -313,7 +330,8 @@ dayjs.extend(utc);
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
/* position: relative; */
|
||||
flex: 1;
|
||||
background-attachment: local;
|
||||
background-image: linear-gradient(to right, var(--bg-0) 5px, transparent 2px), linear-gradient(var(--bg-4) 2px, transparent 2px);
|
||||
background-size: 10px 1lh;
|
||||
@ -326,6 +344,7 @@ dayjs.extend(utc);
|
||||
margin-trim: block;
|
||||
|
||||
p { margin-block: 1lh; }
|
||||
a, del, s { text-decoration-thickness: 2px; }
|
||||
|
||||
@supports not (margin-trim: block) {
|
||||
:first-child { margin-block-start: 0; }
|
||||
|
@ -1,7 +1,6 @@
|
||||
---
|
||||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import Layout from "./Layout.astro";
|
||||
import formatDate from "@/utils/formatDate";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
@ -12,20 +11,22 @@ interface Props {
|
||||
}
|
||||
|
||||
const { title, ficTitle, date, notes, lastModified }: Props = Astro.props;
|
||||
dayjs.extend(utc);
|
||||
---
|
||||
<Layout title={`${ficTitle}, ${title}`}>
|
||||
<Fragment slot="head">
|
||||
<meta name="description" content={notes} />
|
||||
</Fragment>
|
||||
<slot name="breadcrumbs" />
|
||||
|
||||
<main>
|
||||
<header>
|
||||
<h1>{title}</h1>
|
||||
<time datetime={dayjs(date).utc(true).toISOString()}>
|
||||
Published on {dayjs(date).utc(true).format("MMMM DD, YYYY")}
|
||||
<time datetime={formatDate(date, true)}>
|
||||
Published on {formatDate(date)}
|
||||
</time>
|
||||
{lastModified && (
|
||||
<time datetime={dayjs(lastModified).utc(true).toISOString()}>
|
||||
Last edited on {dayjs(lastModified).utc(true).format("MMMM DD, YYYY")}
|
||||
<time datetime={formatDate(lastModified, true)}>
|
||||
Last edited on {formatDate(lastModified)}
|
||||
</time>
|
||||
)}
|
||||
{notes && (
|
||||
|
@ -10,10 +10,10 @@ const { title = "haetae" }: Props = Astro.props;
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<meta name="pinterest" content="nopin nohover" />
|
||||
<slot name="header" />
|
||||
<meta name="robots" content="noai, noimageai" />
|
||||
<slot name="head" />
|
||||
<title>{title}</title>
|
||||
<script is:inline>
|
||||
localStorage.getItem("theme")
|
||||
|
@ -15,7 +15,7 @@ i used a bunch of assets that require attribution but i also wanted to link peop
|
||||
## images
|
||||
- [Pikachu portrait](https://sprites.pmdcollab.org/#/0025?form=0) by SPIKE CHUNSOFT, curated by the PMD Collab project
|
||||
- [This about page's frame images](https://www.spriters-resource.com/ds_dsi/pokemonmysterydungeonexplorersoftimedarkness/sheet/5986/) by redblueyellow
|
||||
- The blog's frame images and additional assets (such as the mood emoticons) were graciously provided by AymShade on the MapleLegends forums! I can't directly link because I'm scared of a Certain Company™ being evil
|
||||
- the blog's frame images and additional assets (such as the mood emoticons) were graciously provided by AymShade on the MapleLegends forums! i can't directly link because i'm scared of a Certain Company™ being evil
|
||||
- [The guestbook's speech bubble images](https://www.spriters-resource.com/game_boy_advance/mlss/sheet/7573/) by MajinPiccolo
|
||||
|
||||
## fonts
|
||||
@ -25,4 +25,4 @@ i used a bunch of assets that require attribution but i also wanted to link peop
|
||||
- [PixelMplus](https://itouhiro.hatenablog.com/entry/20130602/font) by itouhiro is licensed under [M+ font license](https://web.archive.org/web/20221024231351/http://mplus-fonts.osdn.jp/mplus-bitmap-fonts/#license)
|
||||
- [Redaction](https://www.redaction.us/) by [Forest Young](https://www.moma.org/interactives/exhibitions/2011/talktome/objects/140027/) and [Jeremy Mickel](https://mckltype.com/) is dual-licensed under [LGPL 2.1](https://www.tldrlegal.com/license/gnu-lesser-general-public-license-v2-1-lgpl-2-1) and [OFL 1.1](https://www.tldrlegal.com/license/open-font-license-ofl-explained)
|
||||
- [sq](https://github.com/leahneukirchen/sq) by [Leah Neukirchen](https://leahneukirchen.org/) is in public domain
|
||||
- [Wonder Mail](https://www.dafont.com/wonder-mail.font) by ShinxHijinx is free for personal use (as far as I know at the time of this writing: April 14, 2025)
|
||||
- [Wonder Mail](https://www.dafont.com/wonder-mail.font) by ShinxHijinx is free for personal use (as far as i know at the time of this writing: April 14, 2025)
|
@ -2,12 +2,13 @@
|
||||
import Blog from '@/layouts/Blog.astro';
|
||||
import type { GetStaticPaths } from 'astro';
|
||||
import { getCollection, render } from 'astro:content';
|
||||
import leftNormal from "$/images/left-normal.png";
|
||||
import leftHover from "$/images/left-hover.png";
|
||||
import leftActive from "$/images/left-active.png";
|
||||
import rightNormal from "$/images/right-normal.png";
|
||||
import rightHover from "$/images/right-hover.png";
|
||||
import rightActive from "$/images/right-active.png";
|
||||
|
||||
import leftNormal from "$/left-normal.png";
|
||||
import leftHover from "$/left-hover.png";
|
||||
import leftActive from "$/left-active.png";
|
||||
import rightNormal from "$/right-normal.png";
|
||||
import rightHover from "$/right-hover.png";
|
||||
import rightActive from "$/right-active.png";
|
||||
|
||||
export const getStaticPaths = (async () => {
|
||||
const blog = await getCollection("blog");
|
||||
@ -27,6 +28,9 @@ const previous = current + 1 === blog.length ? undefined : blog[current + 1];
|
||||
const next = current === 0 ? undefined : blog[current - 1];
|
||||
---
|
||||
<Blog title={entry.data.title!} date={entry.data.pubDate!} currently={entry.data.currently}>
|
||||
<Fragment slot="head">
|
||||
<meta name="description" content={entry.body?.substring(0, 150) + "…"}>
|
||||
</Fragment>
|
||||
<Content />
|
||||
|
||||
{(previous || next) && (
|
||||
|
@ -16,7 +16,9 @@ const getMonth = (id: number) => {
|
||||
}
|
||||
---
|
||||
<Layout>
|
||||
<Fragment slot="header" set:html={`<link rel="alternate" type="application/rss+xml" title="haetae's blog" href=${new URL("/blog/rss.xml", Astro.site)} />`} />
|
||||
<Fragment slot="head">
|
||||
<link rel="alternate" type="application/rss+xml" title="haetae's blog" href={new URL("/blog/rss.xml", Astro.site)} />
|
||||
</Fragment>
|
||||
<h1>blog</h1>
|
||||
<ul>
|
||||
{Object.entries(sorted).map(entry => (
|
||||
|
@ -1,22 +1,37 @@
|
||||
import type { APIRoute } from "astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import rss from "@astrojs/rss";
|
||||
import rss, { type RSSFeedItem } from "@astrojs/rss";
|
||||
import MarkdownIt from "markdown-it";
|
||||
import { parse as htmlParser } from "node-html-parser";
|
||||
import sanitize from "sanitize-html";
|
||||
import fixRssImages from "@/utils/fixRssImages";
|
||||
|
||||
const parser = new MarkdownIt();
|
||||
|
||||
export const GET: APIRoute = async (context) => {
|
||||
const blog = await getCollection("blog");
|
||||
const feed: RSSFeedItem[] = [];
|
||||
|
||||
for (const entry of blog) {
|
||||
const content = parser.render(entry.body!);
|
||||
const html = htmlParser.parse(content);
|
||||
const images = html.querySelectorAll("img");
|
||||
|
||||
await fixRssImages(images, context);
|
||||
|
||||
feed.push({
|
||||
link: `/blog/${entry.id}`,
|
||||
content: sanitize(html.toString(), {
|
||||
allowedTags: sanitize.defaults.allowedTags.concat(["img"]),
|
||||
}),
|
||||
...entry.data,
|
||||
});
|
||||
}
|
||||
|
||||
return rss({
|
||||
title: "haetae's blog",
|
||||
description: "a blog about a weirdo who likes coding",
|
||||
site: context.site!,
|
||||
items: blog.map(entry => ({
|
||||
link: `/blog/${entry.id}`,
|
||||
content: sanitize(parser.render(entry.body!), {
|
||||
allowedTags: sanitize.defaults.allowedTags.concat(["img"]),
|
||||
}),
|
||||
...entry.data,
|
||||
})),
|
||||
items: feed,
|
||||
});
|
||||
}
|
@ -3,8 +3,6 @@ import Chapter from "@/layouts/Chapter.astro";
|
||||
import type { GetStaticPaths } from "astro";
|
||||
import { getCollection, render } from "astro:content";
|
||||
import { Breadcrumbs } from "astro-breadcrumbs";
|
||||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
|
||||
export const getStaticPaths = (async () => {
|
||||
const chapters = await getCollection("chapters");
|
||||
@ -21,7 +19,6 @@ const { ficId, chapterId } = Astro.params;
|
||||
const { chapter } = Astro.props;
|
||||
const { Content, remarkPluginFrontmatter } = await render(chapter);
|
||||
|
||||
dayjs.extend(utc);
|
||||
const chapters = await getCollection("chapters", ({ id }) => {
|
||||
return id.split("/")[0] === ficId;
|
||||
});
|
||||
|
@ -2,8 +2,7 @@
|
||||
import type { GetStaticPaths } from "astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import formatDate from "@/utils/formatDate";
|
||||
|
||||
export const getStaticPaths = (async () => {
|
||||
const fics = await getCollection("fics");
|
||||
@ -19,10 +18,11 @@ const chapters = await getCollection("chapters", ({ id }) => {
|
||||
});
|
||||
chapters.length = Math.min(chapters.length, 5);
|
||||
chapters.sort((a, b) => a.data.sortOrder - b.data.sortOrder);
|
||||
dayjs.extend(utc);
|
||||
---
|
||||
<Layout title={fic.data.title}>
|
||||
<Fragment slot="header" set:html={`<link rel="alternate" type="application/rss+xml" title="${fic.data.title}" href=${new URL(`/fics/${Astro.params.ficId}/rss.xml`, Astro.site)} />`} />
|
||||
<Fragment slot="head">
|
||||
<link rel="alternate" type="application/rss+xml" title={fic.data.title} href={new URL(`/fics/${Astro.params.ficId}/rss.xml`, Astro.site)} />
|
||||
</Fragment>
|
||||
<main>
|
||||
<section>
|
||||
<h1>{fic.data.title}</h1>
|
||||
@ -31,9 +31,9 @@ dayjs.extend(utc);
|
||||
<span class="title">Fandom</span>
|
||||
<span class="data">{fic.data.series.join(", ")}</span>
|
||||
</p>
|
||||
<time datetime={dayjs(fic.data.publishedAt).utc(true).toISOString()}>
|
||||
<time datetime={formatDate(fic.data.publishedAt, true)}>
|
||||
<span class="title">Date</span>
|
||||
<span class="data">{dayjs(fic.data.publishedAt).utc(true).format("MMMM DD, YYYY")}</span>
|
||||
<span class="data">{formatDate(fic.data.publishedAt)}</span>
|
||||
</time>
|
||||
<div>
|
||||
<span class="title">Summary</span>
|
||||
|
@ -1,10 +1,12 @@
|
||||
import type { APIRoute } from "astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import rss from "@astrojs/rss";
|
||||
import rss, { type RSSFeedItem } from "@astrojs/rss";
|
||||
import MarkdownIt from "markdown-it";
|
||||
import { parse as htmlParser } from "node-html-parser";
|
||||
import sanitize from "sanitize-html";
|
||||
const parser = new MarkdownIt();
|
||||
import fixRssImages from "@/utils/fixRssImages";
|
||||
|
||||
const parser = new MarkdownIt();
|
||||
const fics = await getCollection("fics");
|
||||
|
||||
export const GET: APIRoute = async (context) => {
|
||||
@ -12,21 +14,33 @@ export const GET: APIRoute = async (context) => {
|
||||
return id.split("/")[0] === context.params.ficId;
|
||||
});
|
||||
const fic = fics.find(({ id }) => id === context.params.ficId);
|
||||
const feed: RSSFeedItem[] = [];
|
||||
|
||||
for (const entry of chapters) {
|
||||
const content = parser.render(entry.body!);
|
||||
const html = htmlParser.parse(content);
|
||||
const images = html.querySelectorAll("img");
|
||||
|
||||
await fixRssImages(images, context);
|
||||
|
||||
feed.push({
|
||||
link: `/fics/${entry.id}`,
|
||||
title: entry.data.title,
|
||||
pubDate: entry.data.publishedAt,
|
||||
content: sanitize(html.toString(), {
|
||||
allowedTags: sanitize.defaults.allowedTags.concat(["img"]),
|
||||
}),
|
||||
categories: fic?.data.series.concat(fic.data.title),
|
||||
});
|
||||
}
|
||||
|
||||
return rss({
|
||||
title: `${fic?.data.title}`,
|
||||
description: sanitize(parser.render(fic?.data.summary!), {
|
||||
allowedTags: sanitize.defaults.allowedTags.concat(["br"]),
|
||||
}),
|
||||
site: context.site!,
|
||||
items: chapters.map(chapter => ({
|
||||
link: `/fics/${chapter.id}`,
|
||||
title: chapter.data.title,
|
||||
pubDate: chapter.data.publishedAt,
|
||||
content: sanitize(parser.render(chapter.body!), {
|
||||
allowedTags: sanitize.defaults.allowedTags.concat(["img"]),
|
||||
}),
|
||||
categories: fic?.data.series.concat(fic.data.title),
|
||||
})),
|
||||
items: feed,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
---
|
||||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import formatDate from "@/utils/formatDate";
|
||||
import ThemeSwitch from "~/ThemeSwitch.astro";
|
||||
import speech from "$/images/speech.png";
|
||||
import speech from "$/speech.png";
|
||||
|
||||
type GuestBookEntry = {
|
||||
ID: number;
|
||||
@ -18,7 +17,6 @@ type GuestBookEntry = {
|
||||
Guestbook: object;
|
||||
};
|
||||
|
||||
dayjs.extend(utc);
|
||||
const response = await fetch("https://guestbooks.meadow.cafe/api/v1/get-guestbook-messages/500");
|
||||
const data: GuestBookEntry[] = await response.json();
|
||||
data.sort((a, b) => new Date(b.CreatedAt).valueOf() - new Date(a.CreatedAt).valueOf());
|
||||
@ -69,7 +67,7 @@ data.sort((a, b) => new Date(b.CreatedAt).valueOf() - new Date(a.CreatedAt).valu
|
||||
</h1>
|
||||
<time
|
||||
:datetime="formatDate(entry.CreatedAt, true)"
|
||||
x-text="formatDate(entry.CreatedAt)"
|
||||
x-text="formatDate(entry.CreatedAt, false, 'MMMM D, YYYY on dddd, hh:mm a')"
|
||||
>
|
||||
</time>
|
||||
</header>
|
||||
@ -97,8 +95,8 @@ data.sort((a, b) => new Date(b.CreatedAt).valueOf() - new Date(a.CreatedAt).valu
|
||||
: <span>{entry.Name}</span>
|
||||
}
|
||||
</h1>
|
||||
<time datetime={dayjs(entry.CreatedAt).utc(true).toISOString()}>
|
||||
{dayjs(entry.CreatedAt).utc(true).format("MMMM DD, YYYY")}
|
||||
<time datetime={formatDate(entry.CreatedAt, true)}>
|
||||
{formatDate(entry.CreatedAt, false, 'MMMM D, YYYY on dddd, hh:mm a')}
|
||||
</time>
|
||||
</header>
|
||||
|
||||
@ -108,7 +106,6 @@ data.sort((a, b) => new Date(b.CreatedAt).valueOf() - new Date(a.CreatedAt).valu
|
||||
</noscript>
|
||||
</section>
|
||||
|
||||
<button onclick="document.getElementById('notification').showModal()">open popup</button>
|
||||
<dialog id="notification">
|
||||
<form method="dialog">
|
||||
<menu>
|
||||
@ -174,6 +171,8 @@ data.sort((a, b) => new Date(b.CreatedAt).valueOf() - new Date(a.CreatedAt).valu
|
||||
border-image: var(--border) 7 / 7px / 7px repeat;
|
||||
margin: 2rem 0;
|
||||
|
||||
h1 { font-size: 2rem; }
|
||||
|
||||
&.error {
|
||||
max-width: max-content;
|
||||
padding: 1rem;
|
||||
@ -248,9 +247,7 @@ data.sort((a, b) => new Date(b.CreatedAt).valueOf() - new Date(a.CreatedAt).valu
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
dayjs.extend(utc);
|
||||
import formatDate from "@/utils/formatDate";
|
||||
|
||||
const form = document.forms[0];
|
||||
const challenge = form.elements["challengeQuestionAnswer" as any];
|
||||
@ -294,12 +291,6 @@ data.sort((a, b) => new Date(b.CreatedAt).valueOf() - new Date(a.CreatedAt).valu
|
||||
this.errors = "Whoops, couldn't load any guestbook entries! :(";
|
||||
}
|
||||
},
|
||||
formatDate(date: string, iso = false) {
|
||||
if (iso) {
|
||||
return dayjs(date).utc(true).toISOString();
|
||||
} else {
|
||||
return dayjs(date).utc(true).format("MMMM DD, YYYY");
|
||||
}
|
||||
},
|
||||
formatDate: formatDate,
|
||||
}));
|
||||
</script>
|
21
src/utils/fixRssImages.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import type { APIContext } from "astro";
|
||||
import { getImage } from "astro:assets";
|
||||
|
||||
const imagesGlob = import.meta.glob<{ default: ImageMetadata }>("/src/assets/**/*.{jpeg,jpg,png,webp,gif}");
|
||||
|
||||
export default async function (images: any[], context: APIContext) {
|
||||
for (const img of images) {
|
||||
const src = img.getAttribute("src");
|
||||
if (src?.startsWith("$/")) {
|
||||
const cleanedSrc = src.replace("$/", "");
|
||||
const imagePathPrefix = `/src/assets/${cleanedSrc}`;
|
||||
const imagePath = await imagesGlob[imagePathPrefix]();
|
||||
if (imagePath) {
|
||||
const optimizedImage = await getImage({ src: imagePath.default });
|
||||
img.setAttribute("src", context.site + optimizedImage.src.replace("/", ""));
|
||||
} else {
|
||||
throw Error("couldn't find image for rss feed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
13
src/utils/formatDate.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
|
||||
dayjs.extend(utc);
|
||||
|
||||
export default function (date: Date | string, iso = false, format?: string) {
|
||||
if (iso) {
|
||||
return dayjs(date).utc(true).toISOString();
|
||||
} else {
|
||||
// use this as reference: https://day.js.org/docs/en/display/format
|
||||
return dayjs(date).utc(true).format(format ?? "MMMM DD, YYYY");
|
||||
}
|
||||
}
|