diff --git a/app/contact/page.tsx b/app/contact/page.tsx index 1ed2eb8..e596fb7 100644 --- a/app/contact/page.tsx +++ b/app/contact/page.tsx @@ -10,18 +10,18 @@ export default function Contact() { const [submitting, setSubmitting] = useState(false) const [status, setStatus] = useState('') - const nameRef = useRef() - const emailRef = useRef() - const messageRef = useRef() + const nameRef = useRef() + const emailRef = useRef() + const messageRef = useRef() const submit = async (ev: FormEvent) => { ev.preventDefault() setStatus('') - const name: string = nameRef.current?.value ?? '' - const email: string = emailRef.current?.value ?? '' - const message: string = messageRef.current?.value ?? '' + const name = nameRef.current.value + const email = emailRef.current.value + const message = messageRef.current.value if (!name) setStatus(s => s + ' Name required.') if (!email) setStatus(s => s + ' Email required.') diff --git a/app/page.tsx b/app/page.tsx index bc84594..ce8241b 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -39,7 +39,7 @@ export default function Index() {

Note: This is the information for our system in aggregate, - for individual info see our about page + for individual info see our about page

) diff --git a/app/posts/[slug]/page.tsx b/app/posts/[slug]/page.tsx new file mode 100644 index 0000000..2110a35 --- /dev/null +++ b/app/posts/[slug]/page.tsx @@ -0,0 +1,28 @@ +import { notFound } from 'next/navigation' +import Markdown from 'markdown-to-jsx' + +import InfoBar from '~/components/InfoBar' +import { getPostSlugs, loadSinglePage } from '~/utils/post' + +export async function generateStaticParams() { + const slugs = await getPostSlugs() + return slugs.map((slug: string) => ({ slug })) +} + +export default async function Post({ params: { slug } }) { + const post = await loadSinglePage(slug) + if (!post) notFound() + + return ( + <> +

+ {post.title} +

+
+ + {post.body} +
+ + ) +} + diff --git a/app/posts/page.tsx b/app/posts/page.tsx index 34f0e7f..e713259 100644 --- a/app/posts/page.tsx +++ b/app/posts/page.tsx @@ -1,11 +1,26 @@ -export default function Posts() { +import InfoBar from "~/components/InfoBar" +import { Post, getPostSlugs, loadSinglePage } from "~/utils/post" + +export default async function Posts() { + const slugs = await getPostSlugs() + const posts = await Promise.all(slugs.map(loadSinglePage)) + + const sortedPosts = posts.sort((a: Post, b: Post) => b.date.valueOf() - a.date.valueOf()) + return ( <>

Posts

-
-

This will have posts here eventually we promise

+
+ {sortedPosts.map((post: Post) => ( +
+ +

{post.title}

+

{post.subtitle}

+ Read Post => +
+ ))}
) diff --git a/package-lock.json b/package-lock.json index 1223690..0bbb9a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "front-matter": "^4.0.2", + "markdown-to-jsx": "^7.2.0", "next": "^13.4.1", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -224,6 +226,14 @@ "resolved": "https://registry.npmjs.org/animejs/-/animejs-3.2.1.tgz", "integrity": "sha512-sWno3ugFryK5nhiDm/2BKeFCpZv7vzerWUcUPyAZLDhMek3+S/p418ldZJbJXo5ZUOpfm2kP2XRO4NJcULMy9A==" }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, "node_modules/async-validator": { "version": "1.8.5", "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-1.8.5.tgz", @@ -469,6 +479,18 @@ "once": "^1.4.0" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -477,6 +499,14 @@ "node": ">=6" } }, + "node_modules/front-matter": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/front-matter/-/front-matter-4.0.2.tgz", + "integrity": "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==", + "dependencies": { + "js-yaml": "^3.13.1" + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -531,6 +561,18 @@ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -553,6 +595,17 @@ "node": ">=10" } }, + "node_modules/markdown-to-jsx": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.2.0.tgz", + "integrity": "sha512-3l4/Bigjm4bEqjCR6Xr+d4DtM1X6vvtGsMGSjJYyep8RjjIvcWtrXBS8Wbfe1/P+atKNMccpsraESIaWVplzVg==", + "engines": { + "node": ">= 10" + }, + "peerDependencies": { + "react": ">= 0.14.0" + } + }, "node_modules/mimic-response": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", @@ -933,6 +986,11 @@ "node": ">=0.10.0" } }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", diff --git a/package.json b/package.json index 34b4561..4a8d60e 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,8 @@ "author": "", "license": "ISC", "dependencies": { + "front-matter": "^4.0.2", + "markdown-to-jsx": "^7.2.0", "next": "^13.4.1", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/posts/2022-08-01_thoughts-on-neovim.md b/posts/2022-08-01_thoughts-on-neovim.md new file mode 100644 index 0000000..13d5c33 --- /dev/null +++ b/posts/2022-08-01_thoughts-on-neovim.md @@ -0,0 +1,65 @@ +--- +title: Thoughts on Neovim +subtitle: Who even needs an IDE anyways? +author: rose +--- + +## Why I'm using Neovim + +When I first started coding in high school and then later in early +college I used to jump around between editors a lot more than I do today. +I used Notepad++, then Visual Studio, briefly Netbeans, then Atom. + +But since settling into frontend web development I've stayed with VSCode +for a very long time. I liked it because it was straightforward to get +started with, but versatile enough to extend for other languages. +Between various jobs and projects I used it for Javascript, Java, C#, +Rust, and C - and it did admirably at pretty much all of these. + +But about a year ago I saw that VSCode had a Neovim plugin, and I was +intrigued. I'd wanted to get more familiar with Vim beyond the basic +hjkl navigation, and this seemed like a great way to do that! + +So for the last year and change I've had the +vscode-neovim plugin +plugin installed, and I've been really enjoying it! + +I quickly fell in love with visual block mode, or the "delete N words" +commands. They're just so handy I suddenly felt like they were missing +if I needed to edit code any other way! + +But over the weekend I made the jump from using Neovim inside VSCode to +using it more or less on its own. I saw a video that mentioned the +AstroNvim configuration framework and Neovide, and decided "yeah, I think +I want to try that", and a few days later . . . here we are. + +## How is it going? + +Overall, surprisingly well. + +The AstroNvim config I'm using already had NeoTree set up which is +very nice. I've figured out how to get ESLint and Prettier configured +for work, rust-analyzer installed for my own projects, I've been poking +at themes over and over again, and honestly . . . I'm really liking this. + +Getting Neovide to connect to a VM over the network was relatively +straightforward, I love how easy it is to drop my config into git and +keep it synced between computers, and finally having proper mouse support +(which I never could get sorted out with my terminal) is a pretty big +game changer for when I'm just reading code. + +Also, I'd be lying if I said that I didn't love the smooth scrolling and +cursor animation. I am a simple girl after all. + +## Should you try replacing your IDE? + +That is a tricky question to answer. + +I was comfortable spending some time experimenting with this because I +already had decent familiarity with Vim and had been using Neovim +specifically for a while. If you don't have any similar experience, +the learning curve is going to be pretty steep. + +But hey - if you're looking for a challenge, you'll definitely learn +a lot. + diff --git a/posts/2023-01-04_advent-of-wasm.md b/posts/2023-01-04_advent-of-wasm.md new file mode 100644 index 0000000..12a48c8 --- /dev/null +++ b/posts/2023-01-04_advent-of-wasm.md @@ -0,0 +1,75 @@ +--- +title: Advent of Wasm +subtitle: Now with 87% more pain +author: rose +--- + +So the last few years I have done Advent of Code off and on. Sometimes +I have tried to learn a new language, other times I was just trying to +beat my dad each evening. This year though, this year I don't know what +I was thinking. + +It was several weeks after everyone else had started, I had largely written +it off for the year - I was not up for it. Until a terrible idea crossed +my mind. + +Like an intrusive thought, my mind asked: "Well you've been wanting to do +something in web assembly for a while right? How bad could it be?" + +
+ +Turns out I was definitely not ready for this. + +## So what was so hard about it? + +More than anything else, I forgot how much you need to do by hand to do +any sort of assembly. The first day saw me spending several hours just +on some loader code to pass the puzzle input in from JS, call a wasm +function, and then read back the result. + +Next was a few functions for reading numbers out of the wasm memory buffer, +parsing them from ascii, etc. The core read loop was not too tricky, but +the bit that took far longer than it had any reason to was converting my +answer back to ascii and shoving it into an output area. + +Really none of it was surprising, and none of it *should* have +been that hard ... it's just been a while since this Javascript girl +has written truly low-level code. + +To make matters worse I got hard-core distracted by the non-wasm part of +my wasm project. After the first day I returned to my stub JS loader and +expanded it into a little wasm explorer. + +I added a code view, syntax highlighting, auto-loaded my puzzle inputs, +even made a janky little dynamic list that would automatically pick up +new days' solutions as I added them to the repo without needing me to +touch the loader page each day. + +In the end I'm really quite proud of it, I will absolutely be reusing +this setup for future years, and you should +check it out +if you haven't already ... but for wanting to challenge myself with +something new I was doing a lot of the same-old. + +Ultimately I got through 3 days before giving up jusst because every +step along the way involved *so much* extra +code. I may come back to some of the puzzles later, but for now I'm +kind of happy with what I did, and I don't feel like I need to prove +myself by doing more. I was doing it for fun, and so I stopped when +it stopped being fun. + +## Tips if you want to get into writing wasm by hand? + +Uhh ... maybe consider don't? + +Jokes aside: do a throwaway project or two so you get used to passing +data into and out of wasm, whatever parsing you're going to do, etc. + +Do everything in your power to make sure you can focus on the actual +wasm part of your project, because (at least if you're anything like +me) it's easy to get sidetracked with all that. + +With that said: I had fun. Doing new things is always a treat, so if +you're looking for something new to try definitely consider giving +webassembly a look. + diff --git a/styles/layout.css b/styles/layout.css index 4a64adb..6c8578d 100644 --- a/styles/layout.css +++ b/styles/layout.css @@ -33,7 +33,7 @@ header { padding: var(--text-padding); padding-bottom: 0; box-sizing: border-box; - align-items: start; + align-items: flex-start; justify-items: flex-start; } @@ -125,12 +125,15 @@ header:not(.homepage) ~ h1.pageTitle { } main.mainColumn { + min-height: calc(var(--header-overlap) + var(--min-main-overhang)); +} + +.mainColumn { width: var(--main-width); box-sizing: border-box; padding: var(--text-padding); margin: 0 auto; background: var(--main-background); - min-height: calc(var(--header-overlap) + var(--min-main-overhang)); box-shadow: 0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12); } @@ -138,13 +141,17 @@ header.homepage ~ .mainColumn { min-height: calc(var(--header-overlap) + var(--min-main-overhang) - var(--header-bar-height)); } +.mainColumn ~ .mainColumn { + margin-top: calc(2 * var(--text-padding)); +} + main.mainColumn > p:first-child { margin-top: 0; } footer { flex: 1; - align-items: end; + align-items: flex-end; box-sizing: border-box; width: var(--main-width); margin: 0 auto; diff --git a/utils/post.ts b/utils/post.ts new file mode 100644 index 0000000..b2dd328 --- /dev/null +++ b/utils/post.ts @@ -0,0 +1,56 @@ +import { promises as fs } from 'fs' +import path from 'path' + +import frontmatter from 'front-matter' + +export interface Post { + slug: string, + date: Date, + title: string, + subtitle?: string, + author: string, + body: string +} + +const POST_FILE_PATTERN = /^(?[0-9]{4}-[0-9]{2}-[0-9]{2}(-[0-9]{1,2})?)_(?[^\.]+)\.md$/ + +export async function getPostSlugs(): Promise { + const postsDir = path.join(process.cwd(), 'posts') + const postPaths = await fs.readdir(postsDir) + return postPaths.map(getSlugFromFilePath).filter(slug => slug !== null) +} + +export function getSlugFromFilePath(postPath: string): string | null { + const regexResult = POST_FILE_PATTERN.exec(postPath) + if (!regexResult) return null + return regexResult.groups.slug +} + +export async function loadSinglePage(slug: string): Promise { + const postsDir = path.join(process.cwd(), 'posts') + const postPaths = await fs.readdir(postsDir) + const postMatch: RegExpExecArray | null = postPaths + .map((postFile: string) => POST_FILE_PATTERN.exec(postFile)) + .filter((regexResult: RegExpExecArray | null) => regexResult !== null) + .find((regexResult: RegExpExecArray) => regexResult.groups.slug === slug) + + if (!postMatch) return null; + + const fileName = `${postMatch.groups.date}_${postMatch.groups.slug}.md` + const fileContents = (await fs.readFile(path.join(process.cwd(), 'posts', fileName))).toString('utf8') + const { attributes, body } = frontmatter(fileContents) + + type Attributes = { [key: string]: string } + const data: Attributes = attributes as Attributes + + if (!data.title) return null; + + return { + slug: postMatch.groups.slug, + date: new Date(postMatch.groups.date), + title: data.title, + subtitle: data.subtitle, + author: data.author, + body + } +}