aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app.css8
-rw-r--r--src/lib/blog/Post.svelte67
-rw-r--r--src/lib/blog/Post.ts1
-rw-r--r--src/lib/console.ts3
-rw-r--r--src/lib/storage.ts7
m---------src/lib/vendor/storage0
-rw-r--r--src/routes/blog/+layout.svelte6
-rw-r--r--src/routes/blog/+page.svelte96
-rw-r--r--src/routes/blog/[id=int]/+page.svelte9
-rw-r--r--src/routes/blog/_posts/+page.server.ts13
-rw-r--r--src/routes/blog/_posts/+page.svelte32
-rw-r--r--src/routes/blog/base-post.svx1
-rw-r--r--src/routes/blog/posts/alpine-ssh-early-initfs.svx42
-rw-r--r--src/routes/blog/posts/test-post.svx3
-rw-r--r--src/routes/shared.ts29
15 files changed, 255 insertions, 62 deletions
diff --git a/src/app.css b/src/app.css
index c98a44c..4b1b53d 100644
--- a/src/app.css
+++ b/src/app.css
@@ -83,7 +83,7 @@
#postmd {
@apply font-sans;
a {
- @apply quicklink;
+ @apply quicklink text-nowrap;
}
h1 {
@apply text-4xl mb-1.5 mt-2;
@@ -145,7 +145,7 @@
@apply h-[90%] top-[5%];
}
p {
- @apply my-1;
+ @apply my-1.5 leading-5;
}
code {
@apply font-genericmono;
@@ -197,9 +197,9 @@
blockquote:not(.default-blockquote) {
@apply -ml-1 pl-4 p-2 relative my-1;
&::before {
- @apply absolute w-1 bg-accent-primary/50 transition-colors left-0 top-[10%] h-[80%];
+ @apply absolute w-1 bg-violet-500/50 transition-colors left-0 top-[10%] h-[80%];
content: '';
- --colour-full: var(--color-accent-primary);
+ --colour-full: var(--color-violet-500);
}
&:hover::before {
background-color: var(--colour-full);
diff --git a/src/lib/blog/Post.svelte b/src/lib/blog/Post.svelte
index a204c88..ed1b929 100644
--- a/src/lib/blog/Post.svelte
+++ b/src/lib/blog/Post.svelte
@@ -3,6 +3,9 @@
</script>
<script lang="ts">
+ import { dev } from '$app/environment';
+ import { page } from '$app/state';
+
import { parsePostMetadata, type Post } from './Post';
let {
@@ -16,22 +19,53 @@
let meta = $derived(parsePostMetadata(post.metadata));
let PostComp = $derived(post.default);
+
+ let ignorePublishedStatus = $derived(
+ page.url.searchParams.has('ignore-unpublished'),
+ );
</script>
<svelte:head>
- <title>{meta.title} - /~mem/blog</title>
+ <title>{meta.title} - /~mem/blog/</title>
<meta name="description" content={meta.blurb} />
</svelte:head>
+{#snippet unpublished()}
+ <div
+ class="p-4 rounded-lg bg-neutral-950 border-accent-primary border-[1.8px] my-4 sticky top-4 z-50"
+ >
+ <h1 class="text-xl mb-2">Unpublished</h1>
+ <p class="my-1.5 leading-5">
+ This article has either not yet been published or has been retracted.<br
+ />
+ This could be due to being unfinished, factual errors, bad formulations, pending
+ significant corrections, or any number of other reasons.
+ </p>
+ {#if !ignorePublishedStatus && dev}
+ <p class="mt-1 5">
+ <a
+ href={new URL('?ignore-unpublished=+', page.url).href}
+ class="quicklink">Ignore and read anyway</a
+ >
+ </p>
+ {:else if ignorePublishedStatus}
+ <p class="mt-1.5">Here be dragons.</p>
+ {/if}
+ </div>
+{/snippet}
+
<div class="flex justify-center">
<div class="max-w-2xl w-full">
<h1 class="font-space-grotesk text-5xl mt-8 mb-4">
- <span class="text-accent-primary select-none">:&nbsp;</span
- >{meta.title}<span class="text-accent-primary select-none">;</span>
+ <span class="select-none"
+ ><span class="text-red-400">❯ </span> less
+ <span class="text-red-400">'</span></span
+ >{meta.title}<span class="text-accent-primary select-none">';</span>
</h1>
- <p class="my-4 font-genericmono">
- <a href={encodeURI(`../`)} class="quicklink">../</a>{#if filename}<span
- class="select-none">&MediumSpace;▒&MediumSpace;</span
+ <p class="my-4 font-genericmono flex gap-1 md:gap-2 flex-wrap">
+ <a href={encodeURI(`../`)} class="quicklink">../</a
+ >{#if filename && (meta.published || ignorePublishedStatus || dev)}<span
+ class="select-none">▒</span
><a
href={encodeURI(
`https://codeberg.org/dmpmem/tilde/src/branch/master/src/routes/blog/${filename}`,
@@ -39,14 +73,21 @@
class="quicklink"
target="_blank"
rel="noopener noreferrer">src</a
- >{/if}<span class="select-none">&MediumSpace;▒&MediumSpace;</span>ctime: {meta.created
+ >{/if}<span class="select-none">▒</span>ctime: {meta.created
+ .toISOString()
+ .split('T')[0]}<span class="select-none">▒</span>mtime: {meta.updated
.toISOString()
- .split('T')[0]}<span class="select-none"
- >&MediumSpace;▒&MediumSpace;</span
- >mtime: {meta.updated.toISOString().split('T')[0]}
+ .split('T')[0]}
</p>
- <article id="postmd">
- <PostComp />
- </article>
+ {#if meta.published || ignorePublishedStatus}
+ {#if !meta.published}
+ {@render unpublished()}
+ {/if}
+ <article id="postmd">
+ <PostComp />
+ </article>
+ {:else}
+ {@render unpublished()}
+ {/if}
</div>
</div>
diff --git a/src/lib/blog/Post.ts b/src/lib/blog/Post.ts
index 440dffa..aa4c422 100644
--- a/src/lib/blog/Post.ts
+++ b/src/lib/blog/Post.ts
@@ -9,6 +9,7 @@ export type PostMetadata<Parsed extends boolean = false> = {
id: string | number;
created: Parsed extends true ? Date : string;
updated: Parsed extends true ? Date : string;
+ published: boolean | 'unlisted';
};
export type Post<MetadataParsed extends boolean = false> = {
metadata: PostMetadata<MetadataParsed>;
diff --git a/src/lib/console.ts b/src/lib/console.ts
index 82562cd..3e44e4b 100644
--- a/src/lib/console.ts
+++ b/src/lib/console.ts
@@ -115,6 +115,9 @@ margin-right: 4px;
[`WARNING, NUKING IS NOW LEGAL... WORLDWIDE!!! >~<`],
[`Welcome back, Anon`],
[`when you suckin on her nuts`],
+ [`catboys,,,`],
+ [`\${MESSAGE_OF_THE_DAY}`],
+ [`i will mo your td`],
[`wait.. nuking's actually kinda hot......`],
[`you know what happens after a nuke? smoke %c#3`, "color: #ff0000;"],
[`Fucking the industry's transfems since 20xx`],
diff --git a/src/lib/storage.ts b/src/lib/storage.ts
new file mode 100644
index 0000000..0a5334d
--- /dev/null
+++ b/src/lib/storage.ts
@@ -0,0 +1,7 @@
+import { StorageManager } from '$/lib/vendor/storage'
+
+export const localStorage = new StorageManager('local', 'mem.estrogen.zone:');
+export const sessionStorage = new StorageManager('session', 'mem.estrogen.zone:');
+export const memoryStorage = new StorageManager('memory', 'mem.estrogen.zone:');
+
+export const blogStorage = new StorageManager(localStorage, 'blog:');
diff --git a/src/lib/vendor/storage b/src/lib/vendor/storage
new file mode 160000
+Subproject 21e62b1642b299bf6b60d956045e1848486a70d
diff --git a/src/routes/blog/+layout.svelte b/src/routes/blog/+layout.svelte
index 86447b4..61ab5f3 100644
--- a/src/routes/blog/+layout.svelte
+++ b/src/routes/blog/+layout.svelte
@@ -1,4 +1,5 @@
<script lang="ts">
+ import { blogStorage } from '$/lib/storage';
import { page } from '$app/state';
import { onDestroy, onMount, type Snippet } from 'svelte';
@@ -10,10 +11,9 @@
onMount(() => {
const theme =
- page.url.searchParams.get('theme') ??
- localStorage.getItem('mem.estrogen.zone:blog:theme');
+ page.url.searchParams.get('theme') ?? blogStorage.getItem('theme');
if (theme) {
- localStorage.setItem('mem.estrogen.zone:blog:theme', theme);
+ blogStorage.setItem('theme', theme);
document.documentElement.setAttribute('data-blog-theme', theme);
page.url.searchParams.delete('theme');
}
diff --git a/src/routes/blog/+page.svelte b/src/routes/blog/+page.svelte
index 673a371..fb4592a 100644
--- a/src/routes/blog/+page.svelte
+++ b/src/routes/blog/+page.svelte
@@ -3,48 +3,72 @@
import type { PageProps } from './$types';
let { data }: PageProps = $props();
+ let posts = $derived(
+ Object.entries(data.posts)
+ .filter((v) => v[1].metadata.published === true)
+ .toSorted(
+ ([_, a], [__, b]) =>
+ b.metadata.updated.getTime() - a.metadata.updated.getTime(),
+ ),
+ );
</script>
+<svelte:head>
+ <title>/~mem/blog/</title>
+</svelte:head>
+
<div class="flex justify-center">
<div class="max-w-2xl w-full">
<div class="font-genericmono">
- <h1 class="font-space-grotesk text-5xl mt-8 mb-4">
- <span class="text-red-400 select-none">❯&thinsp;</span>ls
- <span class="text-red-400">'</span>blog posts<span class="text-red-400"
- >'</span
- ><span class="text-red-400 select-none">;</span>
- </h1>
- {#each Object.entries(data.posts) as [_filename, post]}
- <div class="flex gap-2">
- <div class="flex flex-col items-end">
- <a
- href={resolve('/blog/[id=int]-[slug]', {
- id: post.metadata.id.toString(),
- slug: post.metadata.slug,
- })}
- class="quicklink">link</a
- >
- <a
- href={resolve('/blog/[id=int]', {
- id: post.metadata.id.toString(),
- })}
- class="quicklink">short</a
- >
- </div>
- <div class="flex flex-col">
- <table>
- <tbody>
- {#each Object.entries( { ...post.metadata, slug: undefined, id: undefined, created: undefined, updated: undefined }, ).filter((v) => v[1] !== undefined) as v}
- <tr>
- <td class="pr-1 align-top">{v[0]}:</td>
- <td class="pl-1 align-top">{JSON.stringify(v[1])}</td>
- </tr>
- {/each}
- </tbody>
- </table>
- </div>
+ {#if posts.length}
+ <h1 class="font-space-grotesk text-5xl mt-8 mb-4">
+ <span class="text-red-400 select-none">❯&thinsp;</span>ls
+ <span class="text-red-400">'</span>blog posts<span
+ class="text-red-400">'</span
+ ><span class="text-red-400 select-none">;</span>
+ </h1>
+ <div class="flex gap-4 flex-col">
+ {#each posts as [_filename, post]}
+ <div class="flex gap-2">
+ <div class="flex flex-col items-end">
+ <a
+ href={resolve('/blog/[id=int]-[slug]', {
+ id: post.metadata.id.toString(),
+ slug: post.metadata.slug,
+ })}
+ class="quicklink">link</a
+ >
+ <a
+ href={resolve('/blog/[id=int]', {
+ id: post.metadata.id.toString(),
+ })}
+ class="quicklink">short</a
+ >
+ </div>
+ <div class="flex flex-col">
+ <table>
+ <tbody>
+ {#each Object.entries( { ...post.metadata, slug: undefined, id: undefined, created: undefined, updated: undefined, published: undefined }, ).filter((v) => v[1] !== undefined) as v}
+ <tr>
+ <td class="pr-1 align-top">{v[0]}:</td>
+ <td class="pl-1 align-top">{JSON.stringify(v[1])}</td>
+ </tr>
+ {/each}
+ </tbody>
+ </table>
+ </div>
+ </div>
+ {/each}
</div>
- {/each}
+ {:else}
+ <h2 class="font-space-grotesk text-5xl mt-8 mb-4">
+ <span class="text-accent-primary select-none">zsh:&ThinSpace;</span>no
+ matches found<span class="text-accent-primary select-none">.</span>
+ </h2>
+ <p class="mt-1.5">
+ Feel free to check back once some posts are published.
+ </p>
+ {/if}
</div>
</div>
</div>
diff --git a/src/routes/blog/[id=int]/+page.svelte b/src/routes/blog/[id=int]/+page.svelte
index 84d33ef..574ea76 100644
--- a/src/routes/blog/[id=int]/+page.svelte
+++ b/src/routes/blog/[id=int]/+page.svelte
@@ -10,22 +10,21 @@
let { data }: PageProps = $props();
- let route = $derived(
+ let route = () =>
forceTrailingSlash(
resolve('/blog/[id=int]-[slug]', {
id: data.post.metadata.id.toString(),
slug: data.post.metadata.slug,
}),
- ),
- );
+ ) + `${page.url.search}${page.url.hash}`;
onMount(() => {
- tick().then(() => replaceState(route, page.state));
+ tick().then(() => replaceState(route(), page.state));
});
</script>
<svelte:head>
- <link rel="canonical" href={new URL(route, page.url).href} />
+ <link rel="canonical" href={new URL(route(), page.url).href} />
</svelte:head>
<Post post={data.post} filename={data.filename} />
diff --git a/src/routes/blog/_posts/+page.server.ts b/src/routes/blog/_posts/+page.server.ts
new file mode 100644
index 0000000..bc9d5d9
--- /dev/null
+++ b/src/routes/blog/_posts/+page.server.ts
@@ -0,0 +1,13 @@
+import { parsePost, type Post } from '$/lib/blog/Post.svelte'
+
+const posts = import.meta.glob("../posts/*.svx") as Record<string, () => Promise<Post>>
+const returnedData = Promise.all(Object.entries(posts).map(v => v[1]().then(r => [v[0], {
+ ...parsePost(r as Post),
+ default: null,
+}] as const)));
+
+export const load = async () => {
+ return {
+ posts: Object.fromEntries(await returnedData)
+ }
+}
diff --git a/src/routes/blog/_posts/+page.svelte b/src/routes/blog/_posts/+page.svelte
new file mode 100644
index 0000000..2f7ca25
--- /dev/null
+++ b/src/routes/blog/_posts/+page.svelte
@@ -0,0 +1,32 @@
+<script lang="ts">
+ import { resolve } from '$app/paths';
+ import type { PageProps } from '../_/$types';
+ import { browser } from '$app/environment';
+ import { onMount, tick } from 'svelte';
+ import { goto } from '$app/navigation';
+
+ let { data }: PageProps = $props();
+ let posts = $derived(Object.entries(data.posts));
+ onMount(() => {
+ requestAnimationFrame(() => goto(resolve('/blog/')));
+ tick().then(() => goto(resolve('/blog/')));
+ });
+</script>
+
+{#if !browser}
+ {#each posts as a}
+ <!-- svelte-ignore a11y_consider_explicit_label -->
+ <a
+ href={resolve('/blog/[id=int]-[slug]', {
+ id: a[1].metadata.id.toString(),
+ slug: a[1].metadata.slug,
+ })}
+ ></a>
+ <!-- svelte-ignore a11y_consider_explicit_label -->
+ <a
+ href={resolve('/blog/[id=int]', {
+ id: a[1].metadata.id.toString(),
+ })}
+ ></a>
+ {/each}
+{/if}
diff --git a/src/routes/blog/base-post.svx b/src/routes/blog/base-post.svx
index 394eaa2..5a0fa13 100644
--- a/src/routes/blog/base-post.svx
+++ b/src/routes/blog/base-post.svx
@@ -8,6 +8,7 @@ id: -1
# Timestamps are in ISO8601 UTC (`date -u +%Y-%m-%dT%H:%M:%SZ`)
created: "2026-01-14T01:25:14Z"
updated: "2026-01-14T01:25:14Z"
+published: true
---
# Base Post
diff --git a/src/routes/blog/posts/alpine-ssh-early-initfs.svx b/src/routes/blog/posts/alpine-ssh-early-initfs.svx
new file mode 100644
index 0000000..7ffc97d
--- /dev/null
+++ b/src/routes/blog/posts/alpine-ssh-early-initfs.svx
@@ -0,0 +1,42 @@
+---
+title: "Launching SSH during early boot with mkinitfs"
+blurb: "Replacing the early init with our own script to launch SSH, killing it in early userspace, and allowing remote disk decryption in the mean time"
+author: "7222e800"
+slug: "alpine-ssh-early-initfs-disk-decryption"
+id: 1768406136
+
+# Timestamps are in ISO8601 UTC (`date -u +%Y-%m-%dT%H:%M:%SZ`)
+created: "2026-01-14T15:53:57Z"
+updated: "2026-01-14T15:53:57Z"
+published: false
+---
+
+For a while, this one's been meaning to setup an early-boot SSH environment for
+Alpine Linux on systems that are using a
+[System Disk](https://wiki.alpinelinux.org/wiki/System_Disk_Mode) installation
+mode.
+
+<!-- TODO: APKOVL boot article -->
+In [Data Disk](https://wiki.alpinelinux.org/wiki/Data_Disk_Mode) mode, it can
+be handled in `boot`, or if needed, `sysinit`. This can even be nicely netbooted
+via a netbooted apkovl - article on that eventually. (for now, if you're
+interested in that, here's a good starting point:
+[alpine/mkinitfs#cc4954b/initramfs-init.in](https://gitlab.alpinelinux.org/alpine/mkinitfs/-/blob/cc4954bc73cf55833b48624232b9c42ca3abc390/initramfs-init.in#L647))
+
+On System Disk installations, with tooling like [dracut](https://wiki.gentoo.org/wiki/Dracut),
+this would also be trivial. Unfortunately, this one's a masochist and like
+staying close to the intended upstream Alpine installation
+
+> **Note**<br/>
+> Alpine does have
+> [a package](https://pkgs.alpinelinux.org/package/v3.23/community/x86_64/dracut)
+> for dracut, and the reader may want to look into using it instead.
+
+## mkinitfs and it's challenges
+
+Alpine's [mkinitfs](https://gitlab.alpinelinux.org/alpine/mkinitfs/) allows us
+to do things like including files or kernel modules in the image, via their
+[features.d](https://gitlab.alpinelinux.org/alpine/mkinitfs/-/tree/master/features.d).
+This is nice and all, but on it's own, we can only really give the kernel a
+module, or a file we manually call by spamming enter through the encryption
+password prompts and running via the 'Emergency Shell'.
diff --git a/src/routes/blog/posts/test-post.svx b/src/routes/blog/posts/test-post.svx
index b12b25a..f828949 100644
--- a/src/routes/blog/posts/test-post.svx
+++ b/src/routes/blog/posts/test-post.svx
@@ -1,5 +1,5 @@
---
-title: "Test Post"
+title: "Test Post: How Testing a Post can Post your Tests"
blurb: "Awawawa Ipsum dolor the neobot is in the washing machine"
author: "7222e800"
slug: "test-post"
@@ -8,6 +8,7 @@ id: -1
# Timestamps are in ISO8601 UTC (`date -u +%Y-%m-%dT%H:%M:%SZ`)
created: "2026-01-14T01:25:14Z"
updated: "2026-01-14T01:25:14Z"
+published: unlisted
---
<script lang="ts">
diff --git a/src/routes/shared.ts b/src/routes/shared.ts
index 2fc9aac..e95ded3 100644
--- a/src/routes/shared.ts
+++ b/src/routes/shared.ts
@@ -391,6 +391,35 @@ export const ttyLines: TTYText[] = (() => {
colour: '#7a7a7a',
},
],
+ ['blog: ', { colour: '#cdcdcd' }],
+ );
+ wait(100);
+ replaceLast((v) => [
+ ...v,
+ [
+ '/~mem/blog/',
+ {
+ colour: '#99aaff',
+ underlined: true,
+ weight: 700,
+ url: 'currenttab:/blog/',
+ },
+ ],
+ [
+ ' (WIP)',
+ {
+ colour: '#999999',
+ },
+ ],
+ ]);
+ wait(100);
+ text(
+ [
+ ' - ',
+ {
+ colour: '#7a7a7a',
+ },
+ ],
['In random hackerspaces', { colour: '#aaaaaa' }],
);
wait(600);