Compare commits

...

10 commits

18 changed files with 289 additions and 61 deletions

View file

@ -4,7 +4,7 @@
"version": "0.0.1",
"scripts": {
"dev": "astro dev",
"start": "astro start -p 8080",
"start": "export $(cat .env.runtime) && node ./dist/server/entry.mjs",
"build": "astro check && astro build",
"preview": "astro preview",
"astro": "astro"

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

View file

@ -2,6 +2,7 @@ import HR from './components/HR.astro'
import MDXImage from './components/mdx/MDXImage.astro'
import MDXCallout from './components/mdx/MDXCallout.astro'
import MDXCodeBlock from './components/mdx/MDXCodeBlock.astro'
import MDXLink from './components/mdx/MDXLink.astro'
export const components = {
hr: HR,
@ -9,4 +10,5 @@ export const components = {
MDXImage: MDXImage,
MDXCallout: MDXCallout,
pre: MDXCodeBlock,
a: MDXLink,
}

View file

@ -1,5 +1,9 @@
---
const p = Astro.props;
const hrClass = "flex-grow w-auto self-baseline h-1 border-t border-crusta-200 dark:border-night-800 m-4 " + p.class;
---
<div class="flex-grow w-auto self-baseline h-1 border-t border-crusta-200 dark:border-night-800 m-4"></div>
<div class={hrClass}></div>

View file

@ -0,0 +1,126 @@
---
// I stole ALLLLLL of this from https://webreaper.dev/posts/astro-accessible-accordion/
interface Props {
title: string;
}
const { title } = Astro.props as Props;
import HR from "../HR.astro";
---
<noscript>
<h2 class="text-xl text-title font-serif">
{ title }
<svg
class="inline-flex h-7 w-7"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
><path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m6 9l6 6l6-6"></path></svg
>
</h2>
<slot/>
</noscript>
<div class="accordion group relative mb-2 rounded-md border border-crusta-900 dark:border-indigo-400 hidden">
<button
class="accordion__button flex w-full flex-1 items-center justify-between gap-2 p-3 text-left font-medium transition hover:text-subtitle sm:px-4"
type="button"
id={`${title} accordion menu button`}
aria-expanded="false"
aria-controls={`${title} accordion menu content`}
>
<span class="text-xl font-serif">{title}</span>
<!-- if using astro and the astro-icon package
<Icon
name="tabler:chevron-down"
aria-hidden="true"
class="accordion__chevron h-7 w-7 shrink-0 transition-transform"
/>
-->
<!-- use this is not using astro-icon (or another SVG you like) -->
<svg
class="accordion__chevron h-7 w-7 shrink-0 transition-transform"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
><path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m6 9l6 6l6-6"></path></svg
>
</button>
<div
id={`${title} accordion menu content`}
aria-labelledby={`${title} accordion menu button`}
class="accordion__content hidden max-h-0 overflow-hidden px-3 transition-all duration-300 ease-in-out sm:px-4"
>
<HR class="mt-0"/>
<div class="prose mb-4 items-center mt-1 max-w-full transition-[height]">
<slot/>
</div>
</div>
</div>
<script>
function accordionSetup() {
const menus = document.querySelectorAll(".accordion") as NodeListOf<HTMLElement>; // set this up on each menu
menus.forEach((menu) => {
menu.classList.remove("hidden");
const button = menu.querySelector(".accordion__button") as HTMLElement; // the clickable banner
const chevron = menu.querySelector(".accordion__chevron") as HTMLElement; // the chevron icon that animates
const content = menu.querySelector(".accordion__content") as HTMLElement; // the stuff that's revealed when open
if (button && content && chevron) {
button.addEventListener("click", (event) => {
if (!menu.classList.contains("active")) { // if closed, stop having it be closed!
menu.classList.add("active");
button.setAttribute("aria-expanded", true);
// we need to set the max height to the height of the accordion object so the animations work correctly
content.classList.remove("hidden");
content.style.maxHeight = content.scrollHeight + "px";
chevron.classList.add("rotate-180");
} else { // if open, stop having it be open!!!!
menu.classList.remove("active");
button.setAttribute("aria-expanded", false);
content.style.maxHeight = '0px';
chevron.classList.remove("rotate-180");
// make text invisible after animation
setTimeout(() => { content.classList.add("hidden") }, 300);
}
event.preventDefault();
return false;
})
}
})
}
accordionSetup();
document.addEventListener("astro:after-swap", accordionSetup);
</script>

View file

@ -0,0 +1,28 @@
---
import Icon from '../Icon.astro'
interface Props {
href: string
class: string
}
const p = Astro.props as Props;
let external = false;
if (p.href.includes('https://') || p.href.includes('http://')) {
external = true;
}
---
<a href={p.href} class={p.class} target={external ? '_blank' : '_self'}>
<slot/>
{external ? (
<>
<Icon icon="FaExternalLink" class='w-2.5 inline-block align-super fill-crusta-800 fill:stroke-night-400' aria-hidden="true"/>
<span class="hidden">opens in a new tab</span>
</>
) : ''}
</a>

View file

@ -4,6 +4,7 @@ date: 2024-09-19
summary: "A quick update post thinking about the blog aspect of my site... and wondering if I'm going about this the right way."
cover: "/blog/banner_sept19.png"
cover_alt: "A mysterious sattelite tower standing in the center of a dense dark fog, a sickly yellow sky peering from behind. Photo by Julian Zwengel on Unsplash."
draft: true
---
<MDXCallout preset="warning">

View file

@ -1,12 +1,16 @@
---
title: Here's a Python Function I Wrote Out of Spite
summary: Importing LoggerPro data into Google Colab is weridly complicated, so I wrote a Python function to fix that. I break down how it works so you can understand how it works even with minimal Python experience.
date: 2024-10-01 6:00:00
date: 2024-11-12 15:35:00
cover: /blog/python-lab/banner-explode2.png
cover_alt: An old laptop exploding. Sloppily edited from a photo by Hugo Clément on Unsplash & a green screen explosion video on YouTube.
toc: true
---
<MDXCallout preset="warning">
This post has not been fully proofread yet. I just wanted to get this out there because I am tired of only having one blog post on my site.
</MDXCallout>
Last week, I did a lab for my intro to physics course. This was the first experimental lab, and so I had to learn how to work a
piece of software named [Vernier LoggerPro](https://www.vernier.com/product/logger-pro-3/). Since we use Vernier sensors for all of our
data collection, we use Vernier's LoggerPro software to actually record the data.

View file

@ -7,7 +7,7 @@ import { toZonedTime } from 'date-fns-tz'
import sfjson from '../data/sharefeed.json'
import type { ShareFeedEntry } from "../lib/utils";
export const baseURL = "localhost:4321"//"https://eleboog.com"
export const baseURL = "https://eleboog.com"
export const author = {
name: "Kebo Kitanari",
email: "kebokyo@eleboog.com",
@ -78,7 +78,9 @@ export async function addPostsToFeed(feed: Feed, main?: boolean) {
.slice(0,20);
posts.forEach((post) => {
const url: string = `${baseURL}/posts/${post.id}`
if (post.data.draft) { return }
const url: string = `${baseURL}/posts/${post.slug}`
let title: string = '';
if (main) {
title = title.concat('[blog] ');

View file

@ -0,0 +1,19 @@
---
export const prerender = true;
import {getCollection} from 'astro:content'
export async function getStaticPaths() {
const archives = await getCollection('archives');
return archives.map(p => ({
params: { slug: p.slug }, props: { p },
}));
}
const { p } = Astro.props;
const { Content } = await p.render();
---
<Content />

View file

@ -46,7 +46,7 @@ const archives = await getCollection('archives');
console.log(toZonedTime(post.data.date, timeZone));
return (
<li>
<a href={'/posts/' + post.slug} class="font-serif text-lg text-crusta-400 dark:text-night-300 hover:underline">{post.data.title}</a>
<a href={'/archives/' + post.slug} class="font-serif text-lg text-crusta-400 dark:text-night-300 hover:underline">{post.data.title}</a>
&emsp;<span class="font-mono text-md">{format(toZonedTime(post.data.date, timeZone), 'LLL d, yyyy')}</span>
<p>{post.data.summary}</p>
</li>

View file

@ -30,6 +30,10 @@ import { timeZone } from "../lib/utils";
</p>
<p class="mb-2">Expect posts once or twice a week, I guess.</p>
<p class="mb-2"><b>Last journal update:</b>
<a href="/journal" class="font-serif text-subtitle text-sm hover:underline">November 14, 2024</a>
</p>
<MDXCallout preset="new">
This site is now using the <a href="https://astro.build">Astro web framework</a>!
Previously, this site was using <a href="https://nextjs.org">Next.js</a>, a fullstack framework built for big whig companies

View file

@ -8,19 +8,21 @@ draft: true
---
import MDXCallout from '../components/mdx/MDXCallout.astro';
import { MdOpenInNew } from "react-icons/md"
import MDXAccordion from '../components/mdx/MDXAccordion.astro';
import Icon from "../components/Icon.astro";
## now
Cramming an entire overhaul of my site before class like a boss
Working on my website instead of taking notes for class because i do not control the hyperfixation.... and also I'm way behind and I am too stressed to catch up right now ;3;
( below is an archive of a "now" post from i don't freaking know, i just haven't updated my site in a while so i wanted to make sure this one could actually be seen for a bit lol )
---
I am currently sitting at my local library. I forgor to bring my library card, so I just browsed through a couple of books and took
pictures of them so I can find them through *completely above-water legit means* in the near future.
I want to get better at drawing, but it's very hard to start. Hopefully the books that I checked out will help a little bit with
getting the ball rolling.
<MDXAccordion title="todo">
- [ ] Figure out how to make the API route serving the Atom feed ([feed.xml](../feeds/feed.xml)) actually send an xml file that can be recognized by the browser (i.e. allowing for a `.xsl` to work with it and/or opening it automatically in your RSS reader of choice).
- [ ] Write *oooooooone* more blog post deliberately **less** involved in order to break my habit of assuming a blog post has to have 5000 characters and five headings to be worth posting
- [ ] Pretty up the python function blog post to fix typos and have clearer instructions (it *is* a tutorial, after all).
- [ ] Set up a dev version of this site & start implementing the account system!
</MDXAccordion>
{/*
---
@ -36,12 +38,44 @@ to implement nicely tho)
---
# 2024-11-14
As I said in my now entry, my hyperfixation has returned to this website. I do not control the hyperfixation, and I wish it would hyperfixate on class instead, but since this is still somewhat productive, I'm okay with it being here for now.
The main thing that I did was add my first "interactive" element: the accordion.
<MDXAccordion title="It looks like this!">
When you click or tap on the box, it unfurls into a section that can contain literally anything I want... like an image of *another* accordion!
![A screenshot of the "todo" accordion I currently have at the top of this page. Unchecked tasks include "Potentially add an extra 'new-tab' flag to MDX links (a space & '+'' after the link in the parentheses? that might be too complicated
to implement nicely tho)" and "Set up a dev version of this site & start implementing the account system!". Checked tasks include "Make external links open in a new tab. This should be pretty easy to implement." and "Automatically insert an 'external link; icon on links that open in a new tab."](/screenshot_accordion.jpeg)
However, there's a problem with this: it requires Javascript to function. I want my site to be completely functional *without* Javascript. So,
there is a fallback for if JS is disabled.
![A screenshot of that same "todo" accordion but with Javascript disabled. It just looks like a regular section of the page, but with a special icon showing that it was meant to be an accordion.](/screenshot_accordion_noscript.jpeg)
</MDXAccordion>
Now that I know how to make stuff like this and give them noscript fallbacks, I feel much more confident about "Phase 2" of my site (implementing a comments section and account system).
The prototype version of my site with an accounts system should be coming out by mid-December. It will likely be hosted on a completely separate server (not on Hetzner lmao) and require manual approval to sign up. I'll make sure to give you a little badge next to your name in the comments if you help me out with beta testing, though :3
Currently, my ideas for Phase 2 consist of the following:
- Accounts will **not** use passwords. There will be two authentication methods available: passkeys and single sign-on. SSO is the easier of the two, as it just refers to using another site to log onto this one (right now, Google, Microsoft, and Discord are the sites I'm thinking of using for SSO). Passkeys are more secure but not fully ready for prime-time and obscure in documentation. I'm implementing them partly as a challenge to myself and partly as a way to yell at everyone else "HEY!!! THESE ARE COOL!!! WE SHOULD USE THESE MORE!!!!" I'll be sure to make a guide on how to sign up with passkeys and/or add passkeys to an existing account for those who are not in the know yet. Eventually, I will want to fully move my account system to only use passkeys, but I recognize that I can't do that *now* with the current state passkeys are in.
- Even after the beta, I'll likely have account signups operate under a manual approval / application system. Basically, you'll have to prove to me that you aren't a bot or a Nazi, and if I believe you, I'll let you in. This is my way of preventing spam and exploitation of my site.
- I want the accounts system to be flexible enough to where they can be used for things *other* than just my comments section. If I ever make a forum on my site again, the forum will use these accounts. I also want accounts to have *their own profile pages* so if you see someone in the comments you think is cool, you can check out their profile and follow them on other places. I want the comments section to be its own sort of community, and stuff like profile pages helps reinforce that aspect of community imo.
Is this way too much effort for an obscure blog's comments section? Yes. Is this just a way for me get more portfolio work done and justify it with a lofty humanitarian goal? Yes. But I think it will be worth it. It will be another thing to set my blog apart and hopefully encourage more people to check it out... and [possibly give me money](https://ko-fi.com/kebokyo).
Anyways HA IM NOT GOING TO GIVE YOU MUSIC TODAY, [THE NUKETOWN EASTER EGG DROPPED AND IT'S GREAT](https://www.youtube.com/watch?v=BMnjU3UsIS0) ok cya
# 2024-11-12
I did it. This entire site is now running on the [Astro web framework](https://astro.build) instead of Next.js.
There were two main reasons why I chose Next.js for this site originally:
1. I already had *some* experience with it in an older iteration of my website refresh (which you can see in [this blog post](https://eleboog.com/posts/2024-06-16-hello-again)), and
1. I already had *some* experience with it in an older iteration of my website refresh (which you can see in [this blog post](/posts/2024-06-16-hello-again)), and
2. It is by far [the most popular metaframework on the web](https://share.stateofjs.com/share/prerendered?localeId=en-US&surveyId=state_of_js&editionId=js2023&blockId=meta_frameworks_ratios&params=&sectionId=libraries&subSectionId=meta_frameworks) right now.
Both of these made it easier to get started with Next.js, and #2 made it more appealing as a learning / portfolio-enrichment experience.

View file

@ -11,5 +11,5 @@ const newestPost = posts.sort((a, b) => compareDesc(new Date(a.data.date), new D
console.log(newestPost.slug)
export const GET: APIRoute = ({ redirect }) => {
return redirect('http://localhost:4321/posts/' + newestPost.slug, 307);
return redirect('https://eleboog.com/posts/' + newestPost.slug, 307);
}

View file

@ -36,47 +36,50 @@
}
article > h1 {
@apply text-3xl font-serif font-bold text-title mb-3 mt-1
}
article > h2 {
@apply text-2xl font-serif font-semibold text-title mb-2
}
article > h3 {
@apply text-xl font-serif text-title mb-2
}
article > h4 {
@apply text-lg font-serif italic text-title mb-2
}
article > p {
@apply text-black dark:text-night-100 mb-2
}
article > p > a,
article > ul > li > a,
article > ol > li > a {
@apply font-serif text-subtitle text-sm hover:underline data-[level=two]:pl-2 data-[level=three]:pl-4
}
article > blockquote {
@apply p-4 border-s-4 text-muted-foreground bg-muted
}
article > pre {
@apply p-2 bg-neutral-300 bg-opacity-50 dark:bg-slate-800 dark:bg-opacity-100 rounded-lg font-mono overflow-y-scroll mb-2
}
article > p > code {
@apply px-1 bg-neutral-300 bg-opacity-50 dark:bg-slate-600 dark:bg-opacity-100 rounded-md font-mono
}
article > pre > code {
@apply px-0 bg-transparent rounded-none
}
article > ul {
@apply p-2 pl-4 mb-2 list-disc text-black dark:text-night-100
}
article > ol {
@apply ml-2 p-2 pl-4 mb-2 list-decimal text-black dark:text-night-100
}
article > li {
@apply mb-1
}
article > img {
@apply relative -z-10 border-4 border-crusta-200 dark:border-night-800 rounded-lg shadow-lgr shadow-crusta-400/20 dark:shadow-night-400/50 my-2
article {
h1 {
@apply text-3xl font-serif font-bold text-title mb-3 mt-1
}
h2 {
@apply text-2xl font-serif font-semibold text-title mb-2
}
h3 {
@apply text-xl font-serif text-title mb-2
}
h4 {
@apply text-lg font-serif italic text-title mb-2
}
p {
@apply text-black dark:text-night-100 mb-2
}
a,
p > a,
ul > li > a,
ol > li > a {
@apply font-serif text-subtitle text-sm hover:underline data-[level=two]:pl-2 data-[level=three]:pl-4
}
blockquote {
@apply p-4 border-s-4 text-muted-foreground bg-muted
}
pre {
@apply p-2 bg-neutral-300 bg-opacity-50 dark:bg-slate-800 dark:bg-opacity-100 rounded-lg font-mono overflow-y-scroll mb-2
}
p > code {
@apply px-1 bg-neutral-300 bg-opacity-50 dark:bg-slate-600 dark:bg-opacity-100 rounded-md font-mono
}
pre > code {
@apply px-0 bg-transparent rounded-none
}
ul {
@apply p-2 pl-4 mb-2 list-disc text-black dark:text-night-100
}
ol {
@apply ml-2 p-2 pl-4 mb-2 list-decimal text-black dark:text-night-100
}
li {
@apply mb-1
}
img {
@apply relative -z-10 border-4 border-crusta-200 dark:border-night-800 rounded-lg shadow-lgr shadow-crusta-400/20 dark:shadow-night-400/50 my-2
}
}

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M320 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l82.7 0L201.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L448 109.3l0 82.7c0 17.7 14.3 32 32 32s32-14.3 32-32l0-160c0-17.7-14.3-32-32-32L320 0zM80 32C35.8 32 0 67.8 0 112L0 432c0 44.2 35.8 80 80 80l320 0c44.2 0 80-35.8 80-80l0-112c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 112c0 8.8-7.2 16-16 16L80 448c-8.8 0-16-7.2-16-16l0-320c0-8.8 7.2-16 16-16l112 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L80 32z"/></svg>

After

Width:  |  Height:  |  Size: 672 B

View file

@ -5,12 +5,12 @@
"jsxImportSource": "react",
"strictNullChecks": true,
"allowJs": true,
"outDir": "dist",
"types": ["vite/client"],
"exclude": ["dist", "public"],
"paths": {
"@/*": ["./*"],
"@c/*": ["./src/components/*"],
"@l/*": ["./lib/*"],
}
},
}
}