Compare commits

...

157 Commits
js ... master

Author SHA1 Message Date
eggy
63349df587 content: add glorious pain 2024-11-22 23:44:14 -05:00
Daniel Chen
a2f9a719c2 content: add more images 2024-11-14 17:52:20 -05:00
Daniel Chen
dc93f9bba1 content: adjust dongo tenses 2024-11-14 17:40:30 -05:00
Daniel Chen
59f5f942b0 content: add miraidon the dongo 2024-11-14 17:33:03 -05:00
Daniel Chen
8ca4756609 chore: vue 3.4 improvements 2024-10-16 13:30:57 -04:00
Daniel Chen
6bf5d77a01 chore: use reactive props 2024-10-16 13:25:13 -04:00
Daniel Chen
5fd2150beb chore: use modern sass api 2024-10-16 13:09:47 -04:00
Daniel Chen
247e0dcdc7 chore: upgrade deps 2024-10-16 13:06:19 -04:00
Daniel Chen
0331cb284e content: add nano excerpts 2024-09-25 16:44:03 -04:00
eggy
1108f258c1 fix: move tag anchor inside div 2024-05-11 18:05:57 -04:00
eggy
d5a2787f56 content: remove trident 2024-04-20 18:20:01 -04:00
eggy
d5bd3e3be2 fix: allow tags with spaces
surely this will not be a problem later
2024-04-04 14:49:29 -04:00
eggy
62e1d7ef22 content: add detail to nano tag 2024-04-04 14:48:35 -04:00
eggy
e05b8ccd81 fix: sunsetting eifueo image link 2024-03-03 20:53:10 -05:00
eggy
189964c508 chore: remove duplicate head 2024-03-01 16:04:25 -05:00
eggy
f789e792a4 chore: improve docs 2024-03-01 15:50:44 -05:00
eggy
00100b089f feat: add scrolling welcome title
css only!
2024-03-01 15:50:04 -05:00
eggy
47514d48a3 chore: use label over div for accessibility 2024-03-01 13:37:33 -05:00
eggy
194c5343c6 chore: upgrade dependencies 2024-03-01 13:01:21 -05:00
eggy
0f14ad569f chore: fix site name 2024-03-01 00:09:18 -05:00
eggy
68fa501003 doc: nous sommes français maintenant
_je_ pense que c'est incroyablement drôle, non?
2024-03-01 00:08:40 -05:00
eggy
e7acf4bd96 content: add trident short 2024-02-29 23:57:10 -05:00
eggy
3719a20d00 feat: increase text contrast and sizing
not sure if this is a good idea
2024-02-29 23:49:04 -05:00
eggy
37d09f7a49 fix: bad image in primoprod 1 2024-02-29 23:37:06 -05:00
eggy
080525f657 content: add ece 192 wpm 2024-02-29 23:20:57 -05:00
eggy
5b71496c9d chore: remove unused line 2024-02-29 23:14:29 -05:00
eggy
0147fbbf23 feat: break words if there is no space
Resoves #20
2024-02-29 18:50:08 -05:00
eggy
2ad9e41c8b fix: show correct featured works 2024-02-29 18:38:15 -05:00
eggy
69874b977c content: add new short 2024-02-05 22:38:01 -05:00
eggy
4ff7bbc2f7 fix: only show featured star on featured stories 2024-02-05 22:37:53 -05:00
eggy
21c5d573a2 content: remove personally identifying info 2 2024-01-07 17:22:25 -05:00
eggy
c6394fc87d content: remove personally identifying info 2024-01-06 22:52:58 -05:00
eggy
8b38b7c674 content: add primoprod 3 and nano 2023 short 2023-12-29 18:23:38 -05:00
eggy
3bc334f0f3 chore: use builtin proseimg over custom element 2023-12-29 17:34:57 -05:00
eggy
1df49b0b84 feat: rebrand site to 'oeufs'
eggworld is a tad immature for me now. so we must call it oeufs. that'll show them.
2023-12-29 17:00:13 -05:00
eggy
a4c9d71cb1 feat: make star featured clickable 2023-11-29 13:39:04 -05:00
eggy
a0eb2d3220 feat: add featured works 2023-11-19 01:19:43 -05:00
eggy
fc690345cc chore: smarter css 2023-06-11 21:23:58 -04:00
eggy
a5f8b4ca30 feat: allow tags with spaces to be linkable
something like 'unstagnation 2023'
2023-06-11 21:20:41 -04:00
eggy
4339520f89 fix: remove periods from filenames
Resolves #17.
2023-05-31 23:12:22 -04:00
eggy
3b627a16d2 chore: remove unused sitemap module 2023-05-31 23:05:36 -04:00
eggy
be45462f4f chore: upgrade deps 2023-05-31 23:04:05 -04:00
eggy
adbf374010 fix: ensure project descriptions fit at all breakpoints 2023-05-24 13:11:09 -04:00
eggy
1c1cb3bf8a fix: tag cleanup 2023-05-24 13:00:59 -04:00
eggy
74f7bc1002 feat: separate tags with and without spaces
with spaces are non-clickable
2023-05-24 12:46:44 -04:00
eggy
4502c819c3 feat: darken borders in dark mode 2023-05-24 12:33:50 -04:00
eggy
314b0efe3a feat: make tags bolder
easier to read
2023-05-24 12:32:54 -04:00
eggy
ec9609b559 fix: add excerpt tags 2023-05-24 12:30:48 -04:00
eggy
528e09a26f chore: clean up parsedcontent types 2023-05-24 12:25:31 -04:00
eggy
006c1494ca feat: add shadows to boxes
pop, less flat
2023-05-24 12:23:48 -04:00
eggy
4b0950dcd0 feat: shadow all tags 2023-05-24 12:15:47 -04:00
eggy
89d845eebb feat: adjust tag styling
prettier!
2023-05-24 12:14:48 -04:00
eggy
016fbb559f fix: fix panquia fire alignment 2023-05-24 12:08:13 -04:00
eggy
d1f674ee88 fix: add eifueo images 2023-05-23 18:15:31 -04:00
eggy
8948fed9ba content: bye bye eifueo 2023-05-23 18:12:52 -04:00
eggy
b96e7fed67 feat: drop shadow images and reduce padding
looks a lot better
2023-05-23 18:12:14 -04:00
eggy
9e30863015 fix: disable withtop until we find a fix
maybe it's js :(
2023-05-23 16:54:36 -04:00
eggy
87cf20ed48 feat: add 404 page 2023-05-23 16:53:34 -04:00
eggy
df4f7f8630 feat: force full-screen
ensures we don't have odd centred things all the time
2023-05-23 16:53:24 -04:00
eggy
5976b6079a upgrade deps and fix nullables 2023-05-23 16:31:52 -04:00
eggy
304c9d6f36 fix: use html lang 2023-04-18 19:55:55 -04:00
eggy
9d55a16040 fix: downgrade away from nuxt 3.4.1
borks nuxt content
2023-04-18 15:20:35 -04:00
eggy
7a78a89393 fix: add more alt text 2023-04-18 15:09:01 -04:00
eggy
bd3f8f5d02 fix: add alt text 2023-04-18 15:08:37 -04:00
eggy
a6d376db80 feat: use svg over webp when possible 2023-04-18 15:05:11 -04:00
eggy
6086680642 chore: update deps 2023-04-18 14:40:51 -04:00
eggy
cc18627f6c remove albatross
articles are not blog entries
2023-03-25 16:07:40 -04:00
eggy
901405087d feat: personal website is NOT portfolio 2023-03-09 23:49:19 -05:00
eggy
a0ef11330a chore: fix lint issues 2023-03-09 12:14:05 -05:00
eggy
7e85b78c45 fix: mobile padding, do not use top on articles 2023-03-09 11:59:50 -05:00
eggy
e9497b3cdc feat: add better tag map titles 2023-02-25 14:22:25 -05:00
eggy
0b6b798db4 feat: typography attempt 1 2023-02-25 12:33:30 -05:00
eggy
b1a24cef2f fix: update panquia status 2023-01-28 16:21:46 -05:00
eggy
f011718ec6 feat: remove boring projects 2023-01-28 16:19:58 -05:00
eggy
b320212ca1 feat: use webp pillow 2023-01-28 16:17:53 -05:00
eggy
f75a8e4976 feat: add pillow pic 2023-01-28 16:16:24 -05:00
eggy
98941ed5f7 fix: add language images 2023-01-28 16:11:32 -05:00
eggy
655daea2b3 chore: update copyright 2023-01-28 16:11:19 -05:00
eggy
38f6b747d3 chore: update projects 2023-01-28 16:10:44 -05:00
eggy
d4c1a3b515 chore: update mandown license 2023-01-28 15:59:48 -05:00
eggy
a0920dfe0f feat: highlight on hover for most els 2023-01-28 15:59:29 -05:00
eggy
577e00a870 chore: switch away from reactivity transform
also use a released version of nuxt
2023-01-28 15:32:48 -05:00
eggy
a7f38e77ae fix: do not show header on non-article content (2) 2023-01-11 15:14:47 -05:00
eggy
fb03714c2f fix: do not show header on non-article content 2023-01-11 15:10:14 -05:00
eggy
ba49ac0a7d content: add projections tag 2023-01-03 15:21:07 -05:00
eggy
c930fd70ec fix: fix build issues 2023-01-03 15:13:28 -05:00
eggy
59ee896153 chore: update deps 2023-01-03 15:00:08 -05:00
eggy
c019a0fa6d chore: remove unused styles 2022-12-31 20:28:07 -05:00
eggy
806bb9b760 fix: make heading links look nicer 2022-12-31 20:27:53 -05:00
eggy
d7f0cfe8f7 chore: use main instead of div for articles 2022-12-31 17:57:07 -05:00
19fd660df7 add wayland article 2022-12-20 18:57:02 -05:00
2789dc375f Indicate that panquia is on fire 2022-12-19 15:41:40 -05:00
0eef57a376 actually draft it 2022-12-02 21:38:44 -05:00
17f66b52d9 take down nano short
we anonymous boys
2022-12-02 21:30:58 -05:00
24c2fa6a6f feat: shrink hr margin 2022-12-02 01:10:56 -05:00
751033960b fix: remove duplicate dot 2022-12-02 01:06:07 -05:00
6373789413 add the watsfic story 2022-12-02 00:46:00 -05:00
a5142fb00b feat: use time el for date strings
for better machine recognition
2022-12-01 01:24:26 -05:00
156be1cfd4 Fix extra hr 2022-11-29 14:51:32 -05:00
99bb0e9db4 Add november albatross 2022-11-29 14:49:06 -05:00
76f8186385 fix: color revision box 2022-11-01 14:17:14 -04:00
a46bc70591 fix: force footer padding to centre align text 2022-11-01 14:13:23 -04:00
d7934568bd migrate domain 2022-11-01 00:07:14 -04:00
c7cd1fef32 fix: add blog excerpt 2022-10-31 14:20:04 -04:00
631416984c feat: add filter to main page 2022-10-31 14:18:14 -04:00
49b772918b add web frameworks 2022-10-30 21:52:55 -04:00
113680fcec final touch to story 2022-10-28 19:23:56 -04:00
d09a4ccbb6 add high crimes story 2022-10-28 19:20:38 -04:00
689e951e36 add new bird story 2022-10-25 18:08:14 -04:00
702445d090 alcohol: fix typos 2022-10-24 23:44:39 -04:00
36b3ac58d1 shorten wine excerpt 2022-10-24 23:30:30 -04:00
4aed55206a add bird stories 2022-10-24 23:28:49 -04:00
d55899643e add wine albatross article 2022-10-24 23:18:37 -04:00
d047fda51e hide drafts 2022-10-24 23:15:26 -04:00
7e750d3290 add politics as draft 2022-10-24 23:11:29 -04:00
b3c365fdd0 tags: add bird tag description 2022-10-24 23:05:11 -04:00
8b7f3460d7 add new bird story 2022-10-24 23:00:10 -04:00
c4f956af60 remove extra post 2022-10-13 17:18:02 -04:00
1cf48e6c22 add missing unstag 2020 stories 2022-10-13 17:16:25 -04:00
6311f64921 feat: add descriptions to story tags 2022-10-12 21:16:13 -04:00
60895ee176 fix excerpt and front matter
oopsies
2022-10-12 20:49:50 -04:00
641c5461b9 add universe city letter
let's get back into it
2022-10-12 20:47:41 -04:00
78cf87b84c fix: cap git blockchain post excerpt 2022-10-04 11:11:52 -04:00
d252fa91b3 add git blockchain 2022-10-03 13:58:26 -04:00
66c4b32049 add albatross articles
simulpubed
2022-10-02 16:30:35 -04:00
aedc42a9b3 fix(about): remove empty list item 2022-09-20 16:22:43 -04:00
dfd90ba2de fix(about): add name to about page 2022-09-20 16:22:00 -04:00
1c6eb2d6fa feat: remove nuxt-zero-js and version bump nuxt
Nuxt rc10 adds a noScript flag, which does the same thing. Remove unnecessary dependencies.
2022-09-15 08:21:52 -04:00
e79f634d22 feat: add nuxt-zero-js
This saves us one step in the build process although doesn't fix the root problem.
2022-09-11 08:53:26 -04:00
8f65123f32 fix: load current page if Nuxt 3 version selected
Originally, it would attempt to load BASE_URL/Nuxt 3 (2022), which obviously does not exist.
2022-09-07 14:41:24 -04:00
a7efd8b1f6 feat: add revisions in footer
advertise older sites
2022-09-01 22:43:43 -04:00
16eab9d7d8 fix: do not interpolate tag titles
fixes duplicate stories
2022-08-15 21:03:46 -04:00
8474f4ff81 fix: use native color-mode for dark mode
fixes all of our problems haha
2022-08-12 21:16:08 -04:00
b5184838cf feat: switch back to totop default 2022-08-12 21:07:01 -04:00
1e98ce27a9 feat: rename scripts 2022-08-12 21:04:57 -04:00
df5085c41d fix: use correct id 2022-08-12 21:04:50 -04:00
7925c5b4e7 fix: load custom script 2022-08-12 20:47:10 -04:00
a9a555d20a feat: add manual js
i cannot believe this
2022-08-12 20:44:27 -04:00
98a2cee26a chore: update deps 2022-08-12 20:14:37 -04:00
b1ab909c5b feat: change all png to webp
we care about it in primoprod we care more now
2022-08-12 17:58:16 -04:00
0473de4873 fix: remove totop arrow
it's causing too many bugs because why wouldn't it
2022-08-10 21:21:27 -04:00
cc3e33cd2c doc: the pain and suffering 2022-08-10 21:17:29 -04:00
1fb3e222ee fix: PRERENDERING ISN'T EVEN RELEASED YET
https://github.com/nuxt/framework/issues/6411
2022-08-10 21:00:44 -04:00
e7ef4821b9 update lockfile
i hate new software so fucking much
2022-08-10 20:45:05 -04:00
ed0eeef2ab fix: god please 2022-08-10 20:42:51 -04:00
7ec729ba66 feat: add meta descriptions 2022-08-10 20:31:16 -04:00
74cad85292 fix: project images 2022-08-10 20:22:37 -04:00
00a5c8c4ca fix: home page v-bind 2022-08-10 20:12:06 -04:00
694a0b2691 fix: just screw 404 2022-08-10 19:50:50 -04:00
260b1f7f30 fix: unknown project image 2022-08-10 19:46:02 -04:00
0b0cf23796 add 404 2022-08-10 19:40:16 -04:00
762a2519c1 really fix 404 2022-08-10 19:40:10 -04:00
aedf88d446 fix: make 404 work 2022-08-10 19:12:32 -04:00
2e4e4727d1 fix: give up and make multiple 404s 2022-08-10 19:01:25 -04:00
6f8f5dad7d fix: properly 404 2022-08-10 18:56:06 -04:00
04ab476d9b feat: add 404 page 2022-08-10 18:47:39 -04:00
eggy
b59e77f432 Merge pull request 'Nuxt Content 3 migration' (#12) from js into master
Reviewed-on: https://git.eggworld.tk/eggy/public/pulls/12
2022-08-10 18:38:34 -04:00
150 changed files with 11980 additions and 4282 deletions

View File

@ -1,44 +1,36 @@
# Nuxt 3 Minimal Starter # Oeufs?
**WARN: Volar 0.40.0 breaks everything and I don't know why — stick with Volar 0.39.5.** Après le HTML manuscrit et le générateur de site statique — c'est Nuxt!
Look at the [nuxt 3 documentation](https://v3.nuxtjs.org) to learn more. Instructions post-compilation (pendant Nuxt n'a pas le prérendu)
## Setup - Compilez `/script.ts` à `/script.js` (`tsc script.ts -m esnext -t esnext --moduleResolution node`)
Make sure to install the dependencies: Lisez la [documentation de Nuxt](https://v3.nuxtjs.org) pour en savoir plus.
## Installation
Assurez-vous d'installer les dépendances:
```bash ```bash
# yarn # yarn
yarn install yarn install
# npm
npm install
# pnpm
pnpm install --shamefully-hoist
``` ```
## Development Server ## Serveur de développement
Start the development server on http://localhost:3000 Démarrez le serveur de développement sur http://localhost:3000
```bash ```bash
npm run dev yarn dev
``` ```
## Production ## Production
Build the application for production: Prévisualisez la production sur votre système local:
```bash ```bash
npm run build yarn preview
``` ```
Locally preview production build: Lisez la [documentation de déploiement](https://v3.nuxtjs.org/guide/deploy/presets) pour en savoir plus.
```bash
npm run preview
```
Checkout the [deployment documentation](https://v3.nuxtjs.org/guide/deploy/presets) for more information.

26
app.vue
View File

@ -3,29 +3,3 @@
<NuxtPage /> <NuxtPage />
</NuxtLayout> </NuxtLayout>
</template> </template>
<style>
* {
box-sizing: border-box;
/* for that cool wave dark mode effect */
z-index: 1;
position: relative;
}
html,
body,
div#__nuxt {
height: 100%;
width: 100%;
}
:root {
--text-color: #243746;
--bg: #f1e7d0;
}
.dark {
--text-color: #ebf4f1;
--bg: #091a28;
}
</style>

52
assets/css/base.scss Normal file
View File

@ -0,0 +1,52 @@
@import url("https://fonts.googleapis.com/css2?family=Bitter:wght@300;400;600;700;800;900&display=swap");
@mixin headings {
h1,
h2,
h3,
h4,
h5,
h6 {
@content;
}
}
* {
box-sizing: border-box;
// for that cool wave dark mode effect
z-index: 1;
position: relative;
}
html,
body,
div#__nuxt {
height: 100%;
}
main {
flex-grow: 1;
}
:root {
--text-color: #243746;
--bg: #f1e7d0;
}
.dark {
--text-color: #ebf4f1;
--bg: #091a28;
}
.text-bitter {
font-family: Bitter, ui-sans-serif, system-ui, -apple-system,
BlinkMacSystemFont, "Segoe UI", Roboto, "Open Sans", "Helvetica Neue",
sans-serif;
}
.text-article {
font-family: "Source Serif Pro", serif;
line-height: 1.8;
color: #111;
font-size: 1.25rem;
}

22
assets/css/main.scss Normal file
View File

@ -0,0 +1,22 @@
@import "base.scss";
.prose article {
@include headings {
& > a:hover,
& > a:active {
text-decoration: underline;
text-decoration-skip-ink: all;
@apply text-blue-700 dark:text-blue-400;
&::before {
content: "#";
position: absolute;
opacity: 0.5;
left: -2rem;
}
}
}
a:hover {
@apply hover:text-blue-700 dark:hover:text-blue-400;
}
}

1
assets/images/star.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="0" stroke-linecap="round" stroke-linejoin="round" class="feather feather-star"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg>

After

Width:  |  Height:  |  Size: 339 B

View File

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import type { BlogParsedContent } from "@/shared/types"; import type { BlogParsedContent } from "@/shared/types";
import { calcReadingTime, getPrettyDate } from "@/shared/metadata"; import { calcReadingTime } from "@/shared/metadata";
const docs = await queryContent<BlogParsedContent>("/blog") const docs = await queryContent<BlogParsedContent>("/blog")
.sort({ date: 1 }) .sort({ date: 1 })
@ -8,11 +8,10 @@ const docs = await queryContent<BlogParsedContent>("/blog")
.find(); .find();
const latest = docs.at(-1) as BlogParsedContent; const latest = docs.at(-1) as BlogParsedContent;
const prettyDate = getPrettyDate(latest);
</script> </script>
<template> <template>
<div class="prose dark:prose-invert flex"> <div class="prose dark:prose-invert flex onhover">
<HomeStatBox <HomeStatBox
:href="latest._path" :href="latest._path"
color="lightblue" color="lightblue"
@ -21,16 +20,15 @@ const prettyDate = getPrettyDate(latest);
> >
<h2 class="m-0 mt-4 mb-1">{{ latest.title }}</h2> <h2 class="m-0 mt-4 mb-1">{{ latest.title }}</h2>
<p class="text-sm text-gray-500 dark:text-gray-400 m-0"> <p class="text-sm text-gray-500 dark:text-gray-400 m-0">
{{ prettyDate }} · {{ calcReadingTime(latest).minutes }} min read <Date :doc="latest" /> · {{ calcReadingTime(latest).minutes }} min read
</p> </p>
<div class="tag-list mt-1"> <div class="tag-list mt-1">
<Tag <Tag
v-for="(tag, index) in latest.tags" v-for="(tag, index) in latest.tags"
:key="index" :key="index"
:dest="`/tags/blog/${tag}`" :dest="`/tags/blog/${tag}`"
> :name="tag"
{{ tag }} />
</Tag>
</div> </div>
<ContentRenderer <ContentRenderer
tag="article" tag="article"
@ -47,8 +45,12 @@ const prettyDate = getPrettyDate(latest);
</div> </div>
</template> </template>
<style scoped> <style scoped lang="scss">
h2 { h2 {
overflow-wrap: break-word; overflow-wrap: break-word;
} }
div.onhover:hover h2 {
@apply text-blue-700 dark:text-blue-400;
}
</style> </style>

View File

@ -1,44 +1,31 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue";
import IconSun from "@/assets/images/sun.svg?component"; import IconSun from "@/assets/images/sun.svg?component";
import IconMoon from "@/assets/images/moon.svg?component"; import IconMoon from "@/assets/images/moon.svg?component";
const colorMode = useColorMode(); const colorMode = useColorMode();
const isToggled = ref(colorMode.value === "dark");
const toggle = () => { const toggle = () => {
isToggled.value = !isToggled.value; colorMode.preference = colorMode.value === "light" ? "dark" : "light";
colorMode.preference = isToggled.value ? "dark" : "light";
}; };
/*
// it unchecks on refresh and i can't make it automatically
// check itself
const darkToggleEl: Ref<HTMLInputElement> = ref(null);
onMounted(() => {
if (isToggled.value) {
darkToggleEl.value.checked = true;
}
});
*/
</script> </script>
<template> <template>
<label for="toggle" :class="['toggle-wrapper']"> <label
<div :class="['toggle', isToggled ? 'enabled' : 'disabled']"> for="dark-toggle"
class="toggle-wrapper"
aria-label="Dark mode indicator label"
>
<div class="toggle">
<div class="icons"> <div class="icons">
<IconMoon /> <IconMoon />
<IconSun /> <IconSun />
</div> </div>
<input <input
id="toggle" id="dark-toggle"
name="toggle" name="dark-toggle"
type="checkbox" type="checkbox"
:checked="isToggled"
ref="darkToggleEl" ref="darkToggleEl"
aria-label="Toggle dark mode"
@click="toggle" @click="toggle"
/> />
</div> </div>
@ -87,7 +74,7 @@ html.dark .toggle-wrapper {
transition: transform var(--transition), background var(--transition); transition: transform var(--transition), background var(--transition);
} }
.toggle.enabled::before { html.dark .toggle::before {
transform: translateX(calc(var(--scale))); transform: translateX(calc(var(--scale)));
} }

View File

@ -7,8 +7,7 @@ const imgUrl = ref("");
const href = ref(""); const href = ref("");
onMounted(async () => { onMounted(async () => {
const results = (await useFetch(FEED_URL, { initialCache: false })) const results = (await useFetch(FEED_URL)).data as Ref<GithubPushEvent[]>;
.data as Ref<GithubPushEvent[]>;
const latestEvent = results.value.find( const latestEvent = results.value.find(
(event) => event.type === "PushEvent" (event) => event.type === "PushEvent"
) as GithubPushEvent; ) as GithubPushEvent;
@ -21,13 +20,19 @@ onMounted(async () => {
<template> <template>
<div class="prose dark:prose-invert"> <div class="prose dark:prose-invert">
<HomeStatBox <HomeStatBox
:href="href" :href
id="github-commit-a"
color="lightgray" color="lightgray"
darkcolor="slategray" darkcolor="slategray"
title="Latest commit" title="Latest commit"
:clearstyles="true" :clearstyles="true"
> >
<img class="m-0 w-full h-full" :src="imgUrl" /> <img
class="m-0 w-full h-full"
:src="imgUrl"
id="github-commit-img"
alt="Latest GitHub commit"
/>
<!-- <!--
<div> <div>
<h2>{{ title }}</h2> <h2>{{ title }}</h2>

13
components/Date.vue Normal file
View File

@ -0,0 +1,13 @@
<script setup lang="ts">
import { getPrettyDate, getUtcDate } from "~~/shared/metadata";
import type { AnyParsedContent } from "~~/shared/types";
const { doc } = defineProps<{ doc: AnyParsedContent }>();
const prettyDate = getPrettyDate(doc);
const utcDate = getUtcDate(doc);
</script>
<template>
<time pubdate :datetime="utcDate">{{ prettyDate }}</time>
</template>

View File

@ -11,8 +11,17 @@ const getSvgIcon = async (name: string) => {
<template> <template>
<div class="hamburger"> <div class="hamburger">
<input class="checkbox" type="checkbox" id="checkbox" /> <input
<label class="checkbox-label" for="checkbox"> class="checkbox"
type="checkbox"
id="checkbox"
aria-label="Hamburger menu toggle"
/>
<label
class="checkbox-label"
for="checkbox"
aria-label="Hamburger menu indicator label"
>
<svg class="ham ham-rotate" viewBox="0 0 100 100" width="60"> <svg class="ham ham-rotate" viewBox="0 0 100 100" width="60">
<path <path
class="line top" class="line top"
@ -25,7 +34,7 @@ const getSvgIcon = async (name: string) => {
/> />
</svg> </svg>
</label> </label>
<div class="drawer prose dark:prose-invert"> <ul class="drawer prose dark:prose-invert">
<li class="m-0" v-for="(item, index) in navItems" :key="index"> <li class="m-0" v-for="(item, index) in navItems" :key="index">
<!-- stupid vite doesn't let require work <!-- stupid vite doesn't let require work
i should have just hardcoded the navbar items --> i should have just hardcoded the navbar items -->
@ -34,12 +43,13 @@ const getSvgIcon = async (name: string) => {
:src="`/nav/${item.title.toLowerCase()}.svg`" :src="`/nav/${item.title.toLowerCase()}.svg`"
class="m-0" class="m-0"
preload="auto" preload="auto"
:alt="`${item.title} logo`"
/> />
{{ item.title }} {{ item.title }}
</a> </a>
<hr class="m-0 m-2" v-if="index !== navItems.length - 1" /> <hr class="m-2" v-if="index !== navItems.length - 1" />
</li> </li>
</div> </ul>
</div> </div>
</template> </template>

101
components/HeaderLoop.vue Normal file
View File

@ -0,0 +1,101 @@
<script setup lang="ts">
const props = defineProps<{ strings: string[]; class?: string }>();
</script>
<template>
<h1 :class="[props.class, 'text-loop relative text-center w-full h-16']">
<span class="text absolute w-full" v-for="s in props.strings" :key="s">
{{ s }}
</span>
</h1>
</template>
<style scoped lang="scss">
@use "sass:math";
@mixin text-loop($els) {
.text-loop {
overflow: hidden;
$duration: 3s;
@if $els > 1 {
& > span {
display: block;
opacity: 0;
@for $i from 1 through $els {
&:nth-child(#{$i}) {
animation: move-test-#{$i} $duration * $els infinite;
}
}
}
}
}
@for $i from 1 through $els {
@keyframes move-test-#{$i} {
$interval: calc(100% / $els);
$upper_bound: $interval * $i;
$lower_bound: $interval * ($i - 1);
// we try to make the previous exit and the next enter
// at the same time, also taking care of negatives
// for i = 1, this is negative, so start the animation at the end of the cycle
@if $i > 1 {
0% {
opacity: 0;
transform: translateY(100%);
}
#{$lower_bound - $interval * 0.05} {
opacity: 0;
transform: translateY(100%);
}
}
#{$lower_bound} {
opacity: 1;
transform: translateY(0%);
}
#{$lower_bound + $interval * 0.95} {
opacity: 1;
transform: translateY(0%);
}
#{$upper_bound} {
opacity: 0;
transform: translateY(-100%);
}
@if $i == 1 {
// reset el 1
#{100% - $interval * 0.05} {
opacity: 0;
transform: translateY(100%);
}
100% {
opacity: 1;
transform: translateY(0%);
}
} @else {
100% {
opacity: 0;
transform: translateY(-100%);
}
}
}
}
}
/**
* For one element, we have the following pattern. To expand it to 2+
* els, we divide 100% by the number of els and turn on the animation
* only at the correct time.
* -5%: invis
* 0%: vis
* 95%: vis
* 100%: invis
*/
@include text-loop(3);
</style>

View File

@ -1,19 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Color, ViewportLength } from "csstype"; import type { Color, ViewportLength } from "csstype";
// fix ReferenceError: _unref is not defined
// https://github.com/nuxt/framework/issues/5546
import { unref as _unref } from "vue";
const { const {
href,
color = "pink", color = "pink",
darkcolor = "#c88994", darkcolor = "#c88994",
title,
clearstyles = false, clearstyles = false,
forceheight, ...props
} = defineProps<{ } = defineProps<{
href?: string; href?: string;
id?: string;
color?: Color; color?: Color;
darkcolor?: Color; darkcolor?: Color;
title?: string; title?: string;
@ -22,7 +17,7 @@ const {
}>(); }>();
const padding = clearstyles ? "0" : "1rem"; const padding = clearstyles ? "0" : "1rem";
const height = forceheight ?? "100%"; const height = props.forceheight ?? "100%";
// v-bind DOES NOT WORK on initial render // v-bind DOES NOT WORK on initial render
// so unfortunately we have to use the old way // so unfortunately we have to use the old way
@ -36,7 +31,7 @@ const cssVars = {
</script> </script>
<template> <template>
<a class="no-underline inline-block flex flex-col items-stretch" :href="href"> <a class="no-underline inline-block flex flex-col items-stretch" :href :id>
<div class="container box" :style="cssVars"> <div class="container box" :style="cssVars">
<p class="m-0 w-full title">{{ title }}</p> <p class="m-0 w-full title">{{ title }}</p>
<div class="main-content"> <div class="main-content">

View File

@ -1,17 +1,18 @@
<script setup lang="ts"> <script setup lang="ts">
import ColourPicker from "./ColourPicker.vue"; import ColourPicker from "./ColourPicker.vue";
import { navItems } from "@/data/navItems"; import { navItems } from "@/data/navItems";
const props = defineProps<{ activeItem?: string }>();
</script> </script>
<template> <template>
<nav class="flex items-center justify-between"> <nav class="flex items-center justify-between">
<ul> <ul>
<li class="home-text"><a href="/">Eggworld</a></li> <li class="home-text"><a href="/">Oeufs?</a></li>
<li v-for="(item, index) in navItems" :key="index"> <li v-for="(item, index) in navItems" :key="index">
<a :href="item.href" class="flex gap-2"> <a :href="item.href" class="flex gap-2">
<img :src="`/nav/${item.title.toLowerCase()}.svg`" /> <img
:src="`/nav/${item.title.toLowerCase()}.svg`"
:alt="`${item.title} logo`"
/>
{{ item.title }}</a {{ item.title }}</a
> >
</li> </li>
@ -82,9 +83,15 @@ li.home-text {
* { * {
--trans: 0.2s ease; --trans: 0.2s ease;
--box-trans-time: 0.4s; --box-trans-time: 0.4s;
transition: opacity var(--trans), transform var(--trans), gap var(--trans), transition:
width var(--trans), box-shadow var(--box-trans-time) ease, opacity var(--trans),
filter var(--trans), padding-left var(--trans), padding-right var(--trans); transform var(--trans),
gap var(--trans),
width var(--trans),
box-shadow var(--box-trans-time) ease,
filter var(--trans),
padding-left var(--trans),
padding-right var(--trans);
} }
@media screen and (max-width: 600px) { @media screen and (max-width: 600px) {

View File

@ -1,9 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import type { StoryParsedContent, BlogParsedContent } from "@/shared/types"; import type { AnyParsedContent } from "@/shared/types";
import { calcReadingTime, getPrettyDate } from "@/shared/metadata"; import { calcReadingTime } from "@/shared/metadata";
import { SpecialTags } from "@/data/specialTags";
import IconStar from "@/assets/images/star.svg?component";
const { post, type, highlighttags } = defineProps<{ const { post, type } = defineProps<{
post: StoryParsedContent | BlogParsedContent; post: AnyParsedContent;
type: "stories" | "blog"; type: "stories" | "blog";
highlighttags?: string[]; highlighttags?: string[];
}>(); }>();
@ -16,8 +18,13 @@ const descText =
</script> </script>
<template> <template>
<div class="story-card p-4"> <div
<h3 class="m-0"> class="break-words max-w-full rounded-lg p-4 shadow-md border border-2 border-gray-300 dark:border-gray-600"
>
<h3 class="m-0 flex items-center gap-1.5">
<a :href="`/tags/${type}/featured`" v-if="post.tags.includes('featured')">
<IconStar class="fill-yellow-500 outline-none" />
</a>
<a <a
:href="post._path" :href="post._path"
class="no-underline text-left text-2xl sm:text-2xl font-bold hover:text-blue-700 dark:hover:text-blue-400 leading-tight transition" class="no-underline text-left text-2xl sm:text-2xl font-bold hover:text-blue-700 dark:hover:text-blue-400 leading-tight transition"
@ -25,21 +32,20 @@ const descText =
{{ post.title }} {{ post.title }}
</a> </a>
</h3> </h3>
<p class="my-1 text-sm">{{ getPrettyDate(post) }} · {{ descText }}</p> <p class="my-1 text-sm"><Date :doc="post" /> · {{ descText }}</p>
<div class="flex flex-wrap"> <div class="flex flex-wrap">
<template v-for="(tag, index) in post.tags" :key="index">
<Tag <Tag
:dest="`/tags/${type}/${tag}`" :dest="`/tags/${type}/${tag}`"
v-for="(tag, index) in post.tags" :name="tag"
:key="index"
:highlight="highlighttags?.includes(tag)" :highlight="highlighttags?.includes(tag)"
> v-if="!SpecialTags.includes(tag)"
{{ tag }} />
</Tag> </template>
</div> </div>
<ContentRenderer :value="post" :excerpt="true" tag="article"> <ContentRenderer :value="post" :excerpt="true" tag="section">
<template #empty>No excerpt available.</template> <template #empty>No excerpt available.</template>
</ContentRenderer> </ContentRenderer>
<!--<p v-if="!post.nopreview" class="m-0"></p>-->
<div class="text-right" v-if="!post.nopreview"> <div class="text-right" v-if="!post.nopreview">
<a <a
:href="post._path" :href="post._path"
@ -50,12 +56,3 @@ const descText =
</div> </div>
</div> </div>
</template> </template>
<style scoped>
.story-card {
border: 0.1rem solid gray;
max-width: 100%;
border-radius: 0.5rem;
overflow-wrap: break-word;
}
</style>

View File

@ -1,11 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Project } from "@/data/projects"; import type { Project } from "@/data/projects";
const { project, reverse = false } = defineProps<{ import { unref as _unref } from "vue";
const { project } = defineProps<{
project: Project; project: Project;
reverse?: boolean; reverse?: boolean;
}>(); }>();
const imgUrl = `url(/images/projects/${project.img ?? "unknown.png"})`; const imgUrl = project.img ? `url(/images/projects/${project.img})` : "none";
</script> </script>
<template> <template>
@ -14,13 +15,14 @@ const imgUrl = `url(/images/projects/${project.img ?? "unknown.png"})`;
<div class="card-text h-full px-4 py-2"> <div class="card-text h-full px-4 py-2">
<div class="h-full flex flex-col justify-between"> <div class="h-full flex flex-col justify-between">
<div> <div>
<h3 class="m-0">{{ project.name }}</h3> <h3 class="m-0 font-bold font-sans">{{ project.name }}</h3>
<div class="flex gap-1 items-center flex-nowrap"> <div class="flex gap-1 items-center flex-nowrap">
<img <img
class="h-5 w-5 m-0" class="h-5 w-5 m-0"
:src="`/images/langs/${lang}.svg`" :src="`/images/langs/${lang}.svg`"
v-for="(lang, index) in project.langs" v-for="(lang, index) in project.langs"
:key="index" :key="index"
:alt="`${lang} logo`"
/> />
<span <span
class="text-xs text-gray-500 dark:text-gray-300 whitespace-nowrap" class="text-xs text-gray-500 dark:text-gray-300 whitespace-nowrap"
@ -42,7 +44,7 @@ const imgUrl = `url(/images/projects/${project.img ?? "unknown.png"})`;
</div> </div>
</div> </div>
</div> </div>
<div class="card-img h-full p-4 flex" /> <div class="card-img h-full p-4 flex" :style="{ '--imgurl': imgUrl }" />
</div> </div>
</a> </a>
</template> </template>
@ -53,6 +55,10 @@ const imgUrl = `url(/images/projects/${project.img ?? "unknown.png"})`;
width: 100%; width: 100%;
} }
.project-anchor:hover h3 {
@apply text-blue-700 dark:text-blue-400;
}
.card { .card {
border: 0.2rem solid pink; border: 0.2rem solid pink;
background: white; background: white;
@ -87,7 +93,7 @@ html.dark .card-text {
.card-img { .card-img {
width: 75%; width: 75%;
background: v-bind(imgUrl); background: var(--imgurl);
background-color: rgb(255, 237, 241); background-color: rgb(255, 237, 241);
background-position: right 90% top 15%; background-position: right 90% top 15%;
background-size: cover; background-size: cover;
@ -101,7 +107,7 @@ html.dark .card-img {
} }
.desc-text { .desc-text {
width: 139%; width: 140%;
/* 140% is too close */ /* 140% is too close */
transition: width 0.2s ease; transition: width 0.2s ease;
} }
@ -120,7 +126,7 @@ a.unclickable {
} }
.desc-text { .desc-text {
width: 135%; width: 136%;
} }
} }

View File

@ -1,19 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
const props = defineProps<{ const { img } = defineProps<{
name: string; name: string;
href: string; href: string;
img: string; img: string;
unclickable?: boolean; unclickable?: boolean;
broken?: boolean;
}>(); }>();
const imgUrl = `/images/services/${props.img}`; const imgUrl = `/images/services/${img}`;
</script> </script>
<template> <template>
<a :href="unclickable ? '' : href" :class="['no-underline', { unclickable }]"> <a
:href="unclickable ? '' : href"
:class="['no-underline', { unclickable: unclickable || broken, broken }]"
>
<div class="card flex flex-col items-center justify-around"> <div class="card flex flex-col items-center justify-around">
<img class="m-0" :src="imgUrl" /> <img class="m-0" :src="imgUrl" :alt="`${name} logo`" />
<h3 class="m-0">{{ props.name }}</h3> <h3 class="m-0">{{ name }}</h3>
<p class="desc-text text-gray-600 dark:text-gray-200"><slot /></p> <p class="desc-text text-gray-600 dark:text-gray-200"><slot /></p>
</div> </div>
</a> </a>
@ -36,6 +40,26 @@ img {
box-shadow: 0 0.1rem 0.5rem 0 gray; box-shadow: 0 0.1rem 0.5rem 0 gray;
} }
a.broken::before {
content: "PANQUIA IS ON FIRE";
position: absolute;
color: red;
transform: rotate(-40deg);
font-size: 1.5rem;
text-align: center;
z-index: 2;
top: 32.5%;
left: -8%;
width: 125%;
font-family: "Roboto", sans-serif;
font-weight: bold;
}
a.broken > .card {
filter: grayscale(100%);
opacity: 0.4;
}
html.dark .card { html.dark .card {
border: 0.2rem solid rgb(126, 93, 98); border: 0.2rem solid rgb(126, 93, 98);
background: rgb(110, 90, 92); background: rgb(110, 90, 92);

View File

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { type StoryParsedContent } from "@/shared/types"; import type { StoryParsedContent } from "@/shared/types";
import { calcReadingTime, getPrettyDate } from "@/shared/metadata"; import { calcReadingTime } from "@/shared/metadata";
const docs = await queryContent<StoryParsedContent>("/stories") const docs = await queryContent<StoryParsedContent>("/stories")
.sort({ date: 1 }) .sort({ date: 1 })
@ -8,12 +8,10 @@ const docs = await queryContent<StoryParsedContent>("/stories")
.find(); .find();
const latest = docs.at(-1) as StoryParsedContent; const latest = docs.at(-1) as StoryParsedContent;
const prettyDate = getPrettyDate(latest);
</script> </script>
<template> <template>
<div class="prose dark:prose-invert flex"> <div class="prose dark:prose-invert flex onhover">
<HomeStatBox <HomeStatBox
:href="latest._path" :href="latest._path"
color="lightgreen" color="lightgreen"
@ -22,16 +20,15 @@ const prettyDate = getPrettyDate(latest);
> >
<h2 class="m-0 mt-4 mb-1">{{ latest.title }}</h2> <h2 class="m-0 mt-4 mb-1">{{ latest.title }}</h2>
<p class="text-sm text-gray-500 dark:text-gray-400 m-0"> <p class="text-sm text-gray-500 dark:text-gray-400 m-0">
{{ prettyDate }} · {{ calcReadingTime(latest).words.total }} words <Date :doc="latest" /> · {{ calcReadingTime(latest).words.total }} words
</p> </p>
<div class="tag-list mt-1"> <div class="tag-list mt-1">
<Tag <Tag
v-for="(tag, index) in latest.tags" v-for="(tag, index) in latest.tags"
:key="index" :key="index"
:dest="`/tags/stories/${tag}`" :dest="`/tags/stories/${tag}`"
> :name="tag"
{{ tag }} />
</Tag>
</div> </div>
<ContentRenderer <ContentRenderer
tag="article" tag="article"
@ -52,4 +49,8 @@ const prettyDate = getPrettyDate(latest);
h2 { h2 {
overflow-wrap: break-word; overflow-wrap: break-word;
} }
div.onhover:hover h2 {
@apply text-blue-700 dark:text-blue-400;
}
</style> </style>

View File

@ -1,19 +1,26 @@
<script setup lang="ts"> <script setup lang="ts">
const { dest, highlight = false } = defineProps<{ const { highlight } = defineProps<{
name: string;
dest: string; dest: string;
highlight?: boolean; highlight?: boolean;
}>(); }>();
// const isLinkableTag = !props.name.includes(" ");
const isLinkableTag = true;
const tagClass = [
"inline-block text-xs rounded-lg py-1 px-2 mt-1 mr-1 transition border border-pink-200 dark:border-pink-900 border-2 font-medium no-underline",
{ "bg-pink-200 dark:bg-pink-900": highlight },
{ "shadow-md": isLinkableTag },
];
</script> </script>
<template> <template>
<a :href="dest"> <div :class="tagClass">
<div <a :href="dest" v-if="isLinkableTag">
:class="[ {{ name }}
'inline-block text-xs rounded-full py-1 px-2 mt-1 mr-1 bg-gray-300 dark:bg-gray-500 transition',
{ 'bg-yellow-200 dark:bg-yellow-700 shadow-lg': highlight },
]"
>
<slot />
</div>
</a> </a>
<div v-else>
{{ name }}
</div>
</div>
</template> </template>

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
const { src } = defineProps<{ src: string }>(); const { src, alt = "" } = defineProps<{ src: string; alt?: string }>();
const imgSrc = const imgSrc =
src.startsWith("http://") || src.startsWith("https://") src.startsWith("http://") || src.startsWith("https://")
@ -9,7 +9,7 @@ const imgSrc =
<template> <template>
<figure class="flex flex-col items-center"> <figure class="flex flex-col items-center">
<img :src="imgSrc" /> <img :src="imgSrc" class="drop-shadow-lg" :alt="alt" />
<figcaption class="text-center"><Markdown /></figcaption> <figcaption class="text-center" v-if="alt">{{ alt }}</figcaption>
</figure> </figure>
</template> </template>

View File

@ -18,10 +18,9 @@ import { projects } from "@/data/projects";
<!-- this could be in markdown but eh --> <!-- this could be in markdown but eh -->
<p> <p>
Hello! It's very nice to meet you — I'm a student and Linux enthusiast who Hello! It's very nice to meet you — I'm a student who is quite passionate
is quite passionate about some subjects but is quite lazy in every other. about some subjects but is quite lazy in every other.
</p> </p>
<p> <p>
I've dabbled extensively and non-extensively in a variety of topics to I've dabbled extensively and non-extensively in a variety of topics to
play with, including: play with, including:
@ -31,6 +30,7 @@ import { projects } from "@/data/projects";
<li>GUI toolkits very very briefly in GTK, Qt, and Swing</li> <li>GUI toolkits very very briefly in GTK, Qt, and Swing</li>
<li>Linux and server administration</li> <li>Linux and server administration</li>
<li>web development in the form of a Chrome extension and my sites</li> <li>web development in the form of a Chrome extension and my sites</li>
<li>hackathons</li>
<li>Godot Engine Cat Simulator DX</li> <li>Godot Engine Cat Simulator DX</li>
<li>ski instruction</li> <li>ski instruction</li>
<li>writing of literature</li> <li>writing of literature</li>
@ -39,7 +39,9 @@ import { projects } from "@/data/projects";
<p>and other things that I'm forgetting right now.</p> <p>and other things that I'm forgetting right now.</p>
<p> <p>
I have two server machines at home a Dell OptiPlex 780 and a Dell I have two server machines at home a Dell OptiPlex 780 and a Dell
Latitude E5520. Yes, one of them is a laptop. Latitude E5520. One of them is a laptop and
<s>I'm surprised it hasn't burnt up yet </s>
<span class="redphasis">it has burnt up.</span>
</p> </p>
<h3>OptiPlex 780 ("asvyn")</h3> <h3>OptiPlex 780 ("asvyn")</h3>
<ul> <ul>
@ -56,6 +58,10 @@ import { projects } from "@/data/projects";
<li><strong>RAM:</strong> 10 GB</li> <li><strong>RAM:</strong> 10 GB</li>
<li><strong>Storage:</strong> 300 GB hard drive</li> <li><strong>Storage:</strong> 300 GB hard drive</li>
<li><strong>OS:</strong> Arch Linux</li> <li><strong>OS:</strong> Arch Linux</li>
<li>
<strong>Status: </strong>
<span class="redphasis">ON FIRE.</span>
</li>
</ul> </ul>
</div> </div>
</template> </template>
@ -69,4 +75,9 @@ ul {
margin: 0; margin: 0;
line-height: 1.35; line-height: 1.35;
} }
.redphasis {
font-weight: bold;
color: red;
}
</style> </style>

View File

@ -7,52 +7,49 @@
This site is statically generated using This site is statically generated using
<a href="https://v3.nuxtjs.org">Nuxt.js</a> with the help of templates and <a href="https://v3.nuxtjs.org">Nuxt.js</a> with the help of templates and
Markdown because really, writing HTML by hand is tedious and I don't Markdown because really, writing HTML by hand is tedious and I don't
know why I ever tried and its source is available know why I ever tried and its
<a href="https://github.com/potatoeggy/public">here</a>. <a href="https://github.com/potatoeggy/public">source is available here</a
>.
</p> </p>
<!-- i could make this a list but god i'm so tired with nuxt --> <!-- i could make this a list but god i'm so tired with nuxt -->
<div class="flex justify-around flex-wrap gap-8 items-center"> <div class="flex justify-around flex-wrap gap-8 items-center">
<ServiceCard name="Gitea" href="https://git.eggworld.tk" img="gitea.png"> <ServiceCard name="Gitea" href="https://git.eggworld.me" img="gitea.svg">
Self-hosted GitHub Self-hosted GitHub
</ServiceCard> </ServiceCard>
<ServiceCard <ServiceCard
name="Eifueo" name="Eifueo"
href="https://eifueo.eggworld.tk" href="https://eifueo.eggworld.me"
img="eifueo.svg" img="eifueo.svg"
> >
Note collection Note collection
</ServiceCard> </ServiceCard>
<ServiceCard <ServiceCard
name="Primoprod" name="Primoprod"
href="https://primoprod.eggworld.tk" href="https://primoprod.eggworld.me"
img="primogem.png" img="primogem.webp"
> >
Wish simulator Wish simulator
</ServiceCard> </ServiceCard>
<ServiceCard <ServiceCard
name="Calibre" name="Calibre"
href="https://calibre.eggworld.tk" href="https://calibre.eggworld.me"
img="calibre-web.png" img="calibre-web.webp"
> >
Kobo Cloud Kobo Cloud
</ServiceCard> </ServiceCard>
<ServiceCard name="Plex" href="https://plex.eggworld.tk" img="plex.png">
Ad-filled media server
</ServiceCard>
<ServiceCard <ServiceCard
name="Jellyfin" name="Jellyfin"
href="https://jellyfin.eggworld.tk" href="https://jellyfin.eggworld.me"
img="jellyfin.png" img="jellyfin.svg"
> >
FOSS media server FOSS media server
</ServiceCard> </ServiceCard>
<ServiceCard <ServiceCard
name="Minecraft" name="Minecraft"
href="minecraft.eggworld.tk" href="minecraft.eggworld.me"
img="minecraft.png" img="minecraft.svg"
:unclickable="true" unclickable
broken
> >
Whitelisted Whitelisted
</ServiceCard> </ServiceCard>

View File

@ -1,16 +1,13 @@
/** /**
* Set the page title in the format [title] | Eggworld. * Set the page title in the format [title] | [site name].
* @param title The title string. * @param title The title string.
*/ */
export function useTitle(title: string) { export function useTitle(title: string, description?: string) {
useHead({ useHead({
title: `${title} | Eggworld`, title: `${title} | Oeufs?`,
link: [ meta: [
{ rel: "icon", type: "image/x-icon", href: "/favicon.ico" }, { name: "viewport", content: " width=device-width,initial-scale=1" },
{ { name: "description", content: description ?? "" },
rel: "stylesheet",
href: "https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css",
},
], ],
}); });
} }

View File

@ -13,13 +13,11 @@ First, what is Linux? At its heart, it is a [kernel](https://en.wikipedia.org/wi
However, this article will largely focus on **desktop Linux**, which competes with other operating systems such as Windows and macOS. However, this article will largely focus on **desktop Linux**, which competes with other operating systems such as Windows and macOS.
# Complete freedom ## Complete freedom
Perhaps the biggest feature of Linux is its ability to do whatever you want, however you want. After a *tiny* bit of tinkering, you'll be able to set up your computer exactly how you'd like it! Perhaps the biggest feature of Linux is its ability to do whatever you want, however you want. After a *tiny* bit of tinkering, you'll be able to set up your computer exactly how you'd like it!
::image{src=sway-desktop.png} ![A terminal, an emulated Switch game, a game launcher, and a browser all automagically arranged by a tiling window manager. The currently playing song is in the top bar.](sway-desktop.webp)
A terminal, an emulated Switch game, a game launcher, and a browser all automagically arranged by a tiling window manager. The currently playing song is in the top bar.
::
Or, if you aren't the type to spend hours fiddling every little thing, you can choose from a variety of existing default desktop interfaces. Or, if you aren't the type to spend hours fiddling every little thing, you can choose from a variety of existing default desktop interfaces.
@ -33,8 +31,7 @@ Or Plasma's endless customisation:
![](https://upload.wikimedia.org/wikipedia/commons/9/94/KDE_Plasma_5.21_Breeze_Twilight_screenshot.png) ![](https://upload.wikimedia.org/wikipedia/commons/9/94/KDE_Plasma_5.21_Breeze_Twilight_screenshot.png)
::image{src=https://www.omgubuntu.co.uk/wp-content/uploads/2019/07/kde-plasma-desktop.jpg} ![](https://www.omgubuntu.co.uk/wp-content/uploads/2019/07/kde-plasma-desktop.jpg)
::
And this is only the beginning — it's not just appearance you have control over, although both GNOME and Plasma also come with their assortment of applications that have designs that perfectly mesh with the desktop, with global theming letting you click a single button in your settings menu to change colours or styles across all your apps. And this is only the beginning — it's not just appearance you have control over, although both GNOME and Plasma also come with their assortment of applications that have designs that perfectly mesh with the desktop, with global theming letting you click a single button in your settings menu to change colours or styles across all your apps.
@ -42,7 +39,7 @@ Don't like your file manager? Swap it out for one of the dozens out there. Don't
**You can do anything.** **You can do anything.**
# The package manager ## The package manager
Speaking of the update manager… Speaking of the update manager…
@ -56,22 +53,20 @@ Here are just a couple of the graphical stores available:
GNOME Software for GNOME: GNOME Software for GNOME:
::image{src=https://www.omgubuntu.co.uk/wp-content/uploads/2021/02/gnome-software-refresh.jpg} ![](https://www.omgubuntu.co.uk/wp-content/uploads/2021/02/gnome-software-refresh.jpg)
::
Discover for Plasma: Discover for Plasma:
::image{src=https://userbase.kde.org/images.userbase/thumb/2/2d/Discoverappfocus.png/500px-Discoverappfocus.png} ![](https://userbase.kde.org/images.userbase/thumb/2/2d/Discoverappfocus.png/500px-Discoverappfocus.png)
::
By contrast, the Microsoft Store was (is) a complete and utter mess that is nowhere near the integration and experience Linux has had for decades. By contrast, the Microsoft Store was (is) a complete and utter mess that is nowhere near the integration and experience Linux has had for decades.
# Open source ## Open source
Not only that, desktop Linux was built by thousands of volunteers, each contributing their own code to make the best product they can. Because it's completely open source (anyone can see or edit the source code), it's inherently more secure as simply more people are looking at it to fix issues and squash bugs. Not only that, desktop Linux was built by thousands of volunteers, each contributing their own code to make the best product they can. Because it's completely open source (anyone can see or edit the source code), it's inherently more secure as simply more people are looking at it to fix issues and squash bugs.
Learning Linux is a great opportunity to jump into learning more about computers because of the knowledge you gain over time of how your computer works on a fundamental level as you inevitably start troubleshooting *when* something breaks. And perhaps you'll be the one to contribute back upstream to the project too, if you fix a bug or add a new feature, and have your own code distributed around to millions of other users. Learning Linux is a great opportunity to jump into learning more about computers because of the knowledge you gain over time of how your computer works on a fundamental level as you inevitably start troubleshooting *when* something breaks. And perhaps you'll be the one to contribute back upstream to the project too, if you fix a bug or add a new feature, and have your own code distributed around to millions of other users.
# Try it now! ## Try it now!
With dozens of well-maintained versions of Linux operating systems out there, you'll be sure to find one that suits your needs. To try GNOME, [Pop!_OS](https://pop.system76.com/) or [Fedora](https://getfedora.org/en/workstation/download/) provide a seamless out-of-the-box experience. To try Plasma, [Kubuntu](https://kubuntu.org/) is a fantastic starting point. To get a macOS-like feel, [Elementary OS](https://elementary.io/) gives you that Apple vibe while, like every other Linux OS, is completely free of charge, and lets you try it out before you decide to install it. With dozens of well-maintained versions of Linux operating systems out there, you'll be sure to find one that suits your needs. To try GNOME, [Pop!_OS](https://pop.system76.com/) or [Fedora](https://getfedora.org/en/workstation/download/) provide a seamless out-of-the-box experience. To try Plasma, [Kubuntu](https://kubuntu.org/) is a fantastic starting point. To get a macOS-like feel, [Elementary OS](https://elementary.io/) gives you that Apple vibe while, like every other Linux OS, is completely free of charge, and lets you try it out before you decide to install it.

View File

@ -9,6 +9,4 @@ On the desktop, dark mode is an abomination that should be eradicated from appli
Browsers, IDEs, and other applications must be freed from their shadowy chains and returned to light — where they truly belong. Browsers, IDEs, and other applications must be freed from their shadowy chains and returned to light — where they truly belong.
::image{src=light-discord.png} ![Perfect.](light-discord.webp)
Perfect.
::

View File

@ -2,11 +2,12 @@
title: Primoprod Progress Report title: Primoprod Progress Report
date: 2021-08-21 date: 2021-08-21
tags: tags:
- primoprod - primoprod
- tech - tech
- featured
--- ---
Welcome to the very first [Primoprod](https://primoprod.eggworld.tk) progress report! In a similar vein to quite a few open source emulation projects (such as those I follow myself using [emufeed](https://github.com/potatoeggy/emufeed/blob/master/sources.py)), I'll be releasing these tidbits in lieu of daily Unstagnation shorts sometimes. Welcome to the very first [Primoprod](https://primoprod.eggworld.me) progress report! In a similar vein to quite a few open source emulation projects (such as those I follow myself using [emufeed](https://github.com/potatoeggy/emufeed/blob/master/sources.py)), I'll be releasing these tidbits in lieu of daily Unstagnation shorts sometimes.
In this hopefully small series of development notes, I'll be laying out my experiences learning web development as an absolute amateur. In this hopefully small series of development notes, I'll be laying out my experiences learning web development as an absolute amateur.
@ -16,9 +17,9 @@ This report will cover the beginnings of the project to the present day: 16 July
## Introduction ## Introduction
What is Primoprod? Short for "Productivity Primogems", it was born when I noticed that the gacha system employed by games such as *Genshin Impact* could be incredibly addictive, so I decided to see if I could take advantage of it to boost my productivity and at the same time try to learn web development. What is Primoprod? Short for "Productivity Primogems", it was born when I noticed that the gacha system employed by games such as _Genshin Impact_ could be incredibly addictive, so I decided to see if I could take advantage of it to boost my productivity and at the same time try to learn web development.
The basic premise was to assign a given point value for each productive task and be able to spend those points on something or have them progress toward a milestone, so that productive tasks would be incentivised. The aforementioned gacha games and Uber use this to great effect, so I decided to emulate the Wish UI of [*Genshin Impact*](https://genshin.mihoyo.com/en). The basic premise was to assign a given point value for each productive task and be able to spend those points on something or have them progress toward a milestone, so that productive tasks would be incentivised. The aforementioned gacha games and Uber use this to great effect, so I decided to emulate the Wish UI of [_Genshin Impact_](https://genshin.mihoyo.com/en).
And so the project began! I decided to work with Vue.js because of its gentler learning curve compared to Angular and more traditional HTML/CSS/JS separation compared to React. You can tell when I became more comfortable in using Vue's declarative system in the later components compared to, say, [`App.vue`](https://github.com/potatoeggy/primoprod/blob/master/src/App.vue). And so the project began! I decided to work with Vue.js because of its gentler learning curve compared to Angular and more traditional HTML/CSS/JS separation compared to React. You can tell when I became more comfortable in using Vue's declarative system in the later components compared to, say, [`App.vue`](https://github.com/potatoeggy/primoprod/blob/master/src/App.vue).
@ -30,17 +31,15 @@ As my first foray into web development, there were many tools and practices I co
Luckily, I didn't have to make any of these decisions because Vue's [CLI](https://cli.vuejs.org/) gives you a list of sane defaults that you can pick from, and since I was learning for the future, I went with the first option of Vue 3 + Typescript. `vue-cli` even nicely [initialised a git repo for me](https://github.com/potatoeggy/primoprod/commit/9b7d7841806c905e8f580f98d1c95d4732178810)! Luckily, I didn't have to make any of these decisions because Vue's [CLI](https://cli.vuejs.org/) gives you a list of sane defaults that you can pick from, and since I was learning for the future, I went with the first option of Vue 3 + Typescript. `vue-cli` even nicely [initialised a git repo for me](https://github.com/potatoeggy/primoprod/commit/9b7d7841806c905e8f580f98d1c95d4732178810)!
At the time, coming from Python/Java, I opted in to the class components plugin hoping it made it easier to develop for, but later removed it due to a lack of documentation with it for Vue 3. Occasional downsides of newer technologies ¯\\\_(ツ)_/¯. At the time, coming from Python/Java, I opted in to the class components plugin hoping it made it easier to develop for, but later removed it due to a lack of documentation with it for Vue 3. Occasional downsides of newer technologies ¯\\\_(ツ)\_/¯.
The [first few commits](https://github.com/potatoeggy/primoprod/commit/ed9d94b61bf91ea9b82ac4d832dfb2b9ff2efc59) had me playing around until I was comfortable enough to introduce my very [first component](https://github.com/potatoeggy/primoprod/commit/fcbb4068dd3b018db2809ccfcc5381d4ea3ae727): the WishButton. The [first few commits](https://github.com/potatoeggy/primoprod/commit/ed9d94b61bf91ea9b82ac4d832dfb2b9ff2efc59) had me playing around until I was comfortable enough to introduce my very [first component](https://github.com/potatoeggy/primoprod/commit/fcbb4068dd3b018db2809ccfcc5381d4ea3ae727): the WishButton.
::image{src=wish-button-emulated.png} ![](src=wish-button-emulated.webp)
::
I'd say it turned out pretty well! Since I wanted to emulate Genshin's UI, I wanted to match it as closely as I could. These two buttons are made of an image inside of a div relatively positioned with text absolutely positioned inside. Original image for comparison: I'd say it turned out pretty well! Since I wanted to emulate Genshin's UI, I wanted to match it as closely as I could. These two buttons are made of an image inside of a div relatively positioned with text absolutely positioned inside. Original image for comparison:
::image{src=wish-button-original.png} ![](wish-button-original.webp)
::
There are still some differences between the texts since Genshin uses antialiasing, and the alignment and shadow of the icon beside the wish quantity is slightly off too, but I would consider this result to be acceptable. There are still some differences between the texts since Genshin uses antialiasing, and the alignment and shadow of the icon beside the wish quantity is slightly off too, but I would consider this result to be acceptable.
@ -58,10 +57,7 @@ See [GemCounter](https://github.com/potatoeggy/primoprod/blob/master/src/compone
Although I had read up on [MDN's](https://developer.mozilla.org/en-US/) fantastic tutorials/documentation a fair bit and used flexboxes and `rem` everywhere, I apparently did not catch `box-sizing: border-box` and the margins and padding just did not arrange themselves how they should have. Although I had read up on [MDN's](https://developer.mozilla.org/en-US/) fantastic tutorials/documentation a fair bit and used flexboxes and `rem` everywhere, I apparently did not catch `box-sizing: border-box` and the margins and padding just did not arrange themselves how they should have.
::image{src=mdn-box-sizing-tip.png} ![:/ thanks MDN for letting me know](mdn-box-sizing-tip.webp)
::
:/ thanks MDN for letting me know
[Some foreshadowing](https://github.com/potatoeggy/primoprod/blob/master/src/components/ItemRevealScreen.vue#L224) [Some foreshadowing](https://github.com/potatoeggy/primoprod/blob/master/src/components/ItemRevealScreen.vue#L224)
@ -69,13 +65,11 @@ Although I had read up on [MDN's](https://developer.mozilla.org/en-US/) fantasti
Designing the basic screen was pretty straightforward. For all its woes, pure CSS still works and is intuitive enough that my git history was only slightly too messy and I got my results. Designing the basic screen was pretty straightforward. For all its woes, pure CSS still works and is intuitive enough that my git history was only slightly too messy and I got my results.
::image{src=primoprod-wishbanners.png} ![](primoprod-wishbanners.webp)
::
Pretty good, right? Now, the design still isn't adaptive enough *since things get cut off for who knows why I thought flexboxes were supposed to solve all this* but for the most part it looks good enough. It appears I'll have to make a lot of exceptions for mobile devices… Pretty good, right? Now, the design still isn't adaptive enough _since things get cut off for who knows why I thought flexboxes were supposed to solve all this_ but for the most part it looks good enough. It appears I'll have to make a lot of exceptions for mobile devices…
::image{src=primoprod-wishbanners-scaled.png} ![](primoprod-wishbanners-scaled.webp)
::
With some help taken by examining the assets of https://genshin.thekima.com and https://gi-wish-simulator.uzairashraf.dev, I grabbed a static background image as well as the videos! With some help taken by examining the assets of https://genshin.thekima.com and https://gi-wish-simulator.uzairashraf.dev, I grabbed a static background image as well as the videos!
@ -83,7 +77,7 @@ For now, every time you pressed the wish button, you got a five-star animation.
## Asset prefetching and inconsistencies ## Asset prefetching and inconsistencies
Progress was steady and things appeared to be running perfectly. but on my local machine things load instantly, courtesy of it not being beamed around the world. So when it was deployed to https://primoprod.eggworld.tk, even with a relatively fast download speed, the background image loaded slowly, and there were a few seconds of downtime before a video would play. Progress was steady and things appeared to be running perfectly. but on my local machine things load instantly, courtesy of it not being beamed around the world. So when it was deployed to https://primoprod.eggworld.me, even with a relatively fast download speed, the background image loaded slowly, and there were a few seconds of downtime before a video would play.
This was unacceptable, so I investigated around on how Vue prefetches assets before giving up and setting up an [Electron](https://www.electronjs.org/) build for the desktop — this doesn't resolve the issue on the web, but until I'm more competent with Vue I can't quite make the necessary changes. The issue of slow asset loading also severely lengthened the suffering in the `ItemRevealScreen.vue` arc, where animations and audio had to be timed… This was unacceptable, so I investigated around on how Vue prefetches assets before giving up and setting up an [Electron](https://www.electronjs.org/) build for the desktop — this doesn't resolve the issue on the web, but until I'm more competent with Vue I can't quite make the necessary changes. The issue of slow asset loading also severely lengthened the suffering in the `ItemRevealScreen.vue` arc, where animations and audio had to be timed…
@ -136,19 +130,17 @@ No it doesn't it's not even close but I'm not coming back to that
## Sweet release ## Sweet release
Now that `ItemRevealScreen` is *done and over with and will not need any changes*, before making myself work on the equally fun part of the project that is `ItemRevealAllOverlay`, I opted for a break in `ItemObtainScreen` and `ItemDescriptionOverlay` and a one-week hiatus. Now that `ItemRevealScreen` is _done and over with and will not need any changes_, before making myself work on the equally fun part of the project that is `ItemRevealAllOverlay`, I opted for a break in `ItemObtainScreen` and `ItemDescriptionOverlay` and a one-week hiatus.
And they were easy! Easy and fun. It was blissful to be working with structured HTML and CSS again, and the animation pains there were *nothing* compared to the trauma of `ItemRevealScreen`. And they were easy! Easy and fun. It was blissful to be working with structured HTML and CSS again, and the animation pains there were _nothing_ compared to the trauma of `ItemRevealScreen`.
In fact, I consider those two to be 100% done unless I can find a way to apply a border that looks exactly like the original game's that isn't an image. In fact, I consider those two to be 100% done unless I can find a way to apply a border that looks exactly like the original game's that isn't an image.
But it looks great! But it looks great!
::image{src=itemdescriptionoverlay.png} ![](itemdescriptionoverlay.webp)
::
::image{src=itemobtainoverlay.png} ![](itemobtainoverlay.webp)
::
## Wrapping up ## Wrapping up

View File

@ -0,0 +1,52 @@
---
title: "Stay Anonymous Online With These 4 Browser Extensions"
date: "2022-08-06"
_draft: true
tags:
- tech
- albatross
---
This article is [also published in *The FOSS Albatross.*](https://medium.com/the-foss-albatross/stay-anonymous-online-with-these-4-browser-extensions-f032c43f2bb)
It's a given that no matter what you do while browsing the web, Big Tech has their grubby fingers all over your data and tracks every site you visit, selling it to advertisers and other shady actors. Preventing this is almost impossible, short of something like Tor Browser behind a VPN in a virtual machine, which is slow and cumbersome.
<!-- more -->
If you're enamored with the convenience of major browsers, you can still increase your privacy by installing these well-known extensions for Chrome/Firefox, each tackling a different part of web security.
### 1. uBlock Origin
You might already know this extension as a popular ad blocker. However, it can do so much more — in addition to blocking ads, uBlock Origin [blocks trackers and malware sites](https://github.com/gorhill/uBlock) while being one of the lightest ad blockers out there to keep your sites snappy. By blocking trackers, you obviously reduce the amount of data that sites can collect, but by blocking ads, you also don't perform the *request* for the ad, so it's like the ad service doesn't even know you were ever there.
### 2. Decentraleyes
Whenever you connect to virtually any modern website, that website loads JavaScript libraries to make itself interactive, typically from content delivery networks (CDNs) such as Google, Microsoft, and Cloudflare. This, of course, isn't great, because that means that those CDNs know what site you're loading them from.
Enter Decentraleyes! This extension bundles quite a few common libraries so that whenever a site tries to grab it from a CDN, it loads it from Decentraleyes running locally on your computer instead. Privacy benefits aside, this also means that those sites load faster by not having to fetch more files from the web!
### 3. ClearURLs
Have you ever clicked through a bunch of links and ended up with the longest URL you've ever seen, all for a simple search query?
**A simple search for Wikipedia on Google:**
https://www.google.com/search?q=wikipedia&hl=en&ei=Ex7vYq-HI4KH0PEPpKus0Ac&ved=0ahUKEwjv3Lil0rP5AhWCAzQIHaQVC3oQ4dUDCA0&oq=wikipedia&gs_lcp=Cgdnd3Mtd2l6EAxKBAhBGABKBAhGGABQAFgAYABoAHABeACAAQCIAQCSAQCYAQA&sclient=gws-wiz
**A search for dog food on Amazon:**
https://www.amazon.com/s?k=dog+food&crid=2UT0FTXBL16XJ&sprefix=dog+foo%2Caps%2C182&ref=nb_sb_noss_2
All the nonsense after the ampersand (&) are used purely for tracking and won't be caught by content blockers like uBlock Origin because they're *in the initial request itself*. This data includes device and browser information, your search history, how you arrived at the site, and more.
ClearURLs offers a solution by simply removing all of the nonessential parts of a URL before sending it to Google or Amazon so that they never receive this information, securing a little bit more of your privacy.
### 4. NoScript
Lastly, the most intrusive forms of data collection are through JavaScript. So what if you just turned off JavaScript? You'd dramatically improve your browsing experience as sites load much faster, ads don't load at all, and none of the trackers can do a thing, but it could also result in broken sites that rely on it to function at all.
Therefore, if you truly care about privacy and want maximum control over what is allowed to load in your browser, you'll want to be able to selectively enable JavaScript per-script per website. NoScript is just the tool for this, with an easy-to-use interface that streamlines the troubleshooting process when sites require JavaScript.
------
You only need four extensions to dramatically reduce the data sites can collect on you from your browser. Although even these are no match for a properly tuned, custom solution like [LibreWolf](https://librewolf.net/), they provide an excellent first step into becoming more privacy-aware and in securing your data.

View File

@ -0,0 +1,48 @@
---
title: "AV1 — The FOSS Video Codec"
date: 2022-11-13
_draft: true
tags:
- tech
- albatross
---
This article is [also published in *The FOSS Albatross.*](https://medium.com/the-foss-albatross/av1-the-foss-video-codec-1761ad9b0a4a)
More than 60% of all internet traffic is video — YouTube and Netflix are 20% alone! In fact, during COVID, so many people started to watch so much video that that number climbed up to 25%, even as they reduced the quality and size of the videos they served.
This much traffic costs lots of money for internet service providers, who carry on this cost to the streaming service.
And so, media companies, including YouTube and Netflix, have banded together as the Alliance of Open Media (AOM) to develop a video format to reduce the file size of videos: AV1!
<!-- more -->
## How did they shrink video?
Digital video today is represented as a collection of many still pictures known as “frames” that are shown to the viewer really quickly to create the *illusion* of motion.
Each individual frame contains thousands to *millions* of tiny squares called *pixels.*
However, at a typical 24 to 30 frames per second, youd have to store more than a *thousand* frames per minute! Uncompressed, a 1080p would need to stream nearly 50 million pixels per second, or 50 MB/s. You might see how this could grow expensive for both the streaming service and the consumer, *fast*.
So, someone figured out that, hey, we can compress pictures! Why dont we also compress video, too?
And thats exactly what we did — video *codecs* were created to provide a standard format to compress or “encode” video, providing large file size savings at the cost of more resources required to decode them upon playback.
For example, MPEG-2 in DVDs was able to use only 0.25 MB/s of bandwidth for 1080p video when pushed to its limits, though it was a pixelated, smeary mess.
## Why not use H.265?
H.265 is the successor to the ubiquitous H.264 codec that virtually every video player can process. Popularised on many physical media such as Blu-Rays, no browser is currently able to natively play back H.265-encoded content, for one very good reason: MPEG-LA, the organisation that designed it, charges royalties for each of its users.
Understandably, browsers dont want to have to pay millions of dollars recurringly just to play video, so its been stuck playing local media.
Why doesnt H.264 have this problem, you ask? [Cisco generously open sourced](https://en.wikipedia.org/wiki/OpenH264) their implementation of the codec and agreed to pay royalties to MPEG-LA, letting anyone legally use the codec for free.
## Support AV1!
In light of these problems, Google led the development of AV1 as a royalty-free, high quality alternative to H.265. All modern browsers today have already implemented AV1 playback, and hardware released within the last two years all support hardware decoding of AV1, meaning that the performance and power cost of supporting the codec has decreased drastically.
By supporting open standards, you can support a world where outdated royalty models no longer exist, and we can all benefit off of each others work.

View File

@ -0,0 +1,172 @@
---
title: "Etymology of Barin"
date: 2022-10-12
_draft: true
tags:
- barin
---
The [Barin](/tags/stories/barin) universe hosts a variety of different locations. The word "Barin" itself originates as an anagram of the word "brain".
<!-- more -->
## Preton
This major alliance
### Herdit
This nation
### Kora
This kingdom
### Ptuyo
This nation
## Farele
The Farelean Confederacy ("Farele")
### Ciers
This city-state
- Larapelio Avenue
- Nyread Tower
- SleepCity Ciers
- Orwell Library
- Bibliothèque de Lispector
### Leeco
This nation
- Universe City
- Cekendery
- Porter Hall
- Seascout Avenue
- Ira Hagey
- Brian Wright
### Demauge
This nation
- Ceseo Convention Centre
- Data Structure
- Stadio Avenue
- Deathfalcon Drive
- Triplesea Street
- French / FrenchScript
## Enigma
This alliance
### Xunil
This nation
### Weilam
This nation
- Saiyu Sphere
### Eos
This nation
- Emerald Forest
- Laveli Town
### Asvyn
This empire
- Favonius
- Shiganshina
- Kanokari
- Keion
- Shuchiin
- Musani
- Tonikawa
- Ruvinheigen
- Renge Academy
- Biyori
- Sayoasa
- Panquia
- Emina
- The Sinamaria Rose
- Dedication Road
- Kuvira Smith
## Other locations
### Aucervean Mountains
### Moyen Channel
### Ambera Connection (CiersEos)
### English Connection (CiersLeeco)
### Scrivener Connection (CiersXunil)
### Ezwon Connection (CiersDemauge)
### Arslash Connection (CiersHerdit)
## Characters
### Eifueo
### George Anstion
### Siava Pythone
### Minestro
### Nano and Rymo
### Rio Nohigi
### Cloche
### Garson
### Mrs. Lowshi
### Alston
### Brendan May
### Hina Asvyn
### Retadux
### Arro
### Calitea

View File

@ -0,0 +1,54 @@
---
title: "Choosing a License — Politics in FOSS"
date: "2022-09-02"
_draft: true
tags:
- tech
- albatross
---
This article is [also published in *The FOSS Albatross.*](https://medium.com/the-foss-albatross/choosing-a-license-politics-in-foss-df2cbfe48237)
All FOSS projects must have a license before other people can use them — but which should you choose, and what are the consequences behind each one?
<!-- more -->
## You need a license
Under the [Berne Convention](https://en.wikipedia.org/wiki/Berne_Convention) signed by the vast majority of countries in 1886, you own copyright to your software the moment you create it. Rights *exclusive to you* unless given otherwise include the right to copy and modify that software.
In a nutshell, it means that even if you submit your code to GitHub, *no one else* is allowed to copy or redistribute your code (e.g., via forking the repository).
Licenses work around this by granting specific permissions to other people *so long* as they follow the rules in the license.
Although there are many different types and variations of licenses, they can be broadly broken down into two categories: **copyleft** licenses and **permissive** licenses.
## Permissive licenses (e.g., MIT, BSD, Apache)
These licenses are pretty simple: they let anyone do virtually anything with them, so long as they credit you for using your code. This makes it easier for people to incorporate your code into theirs, but also raises the possibility that others, such as corporations, simply take your code without giving back.
Permissively licensed programs tend to be used more often by bigger projects due to the lack of restrictions.
The BSD operating system is a typical example of the dynamic of permissive licenses: Apple used BSD code as a basis for all of their operating systems (macOS, iOS, iPadOS, etc.), as well as Sony in their PlayStations, Nintendo in the Nintendo Switch, and much more. These corporations chose BSD because of how easy it is to take the code for free and build off of it.
## Copyleft licenses (e.g., GPL, MPL, CC-BY-SA)
If you really care about the ideology behind the free software movement, you might consider a copyleft license instead. These require all **derivative works** to have the same license that the original program had. They can still do whatever they want with your code, though.
For example, if library A held a copyleft license and application B bundled it in an app that they published, application B must also be compatible with that copyleft license or else they are in violation of library A's license.
…Which is a lot of words to say that copyleft licenses are "viral" in that they make everything they touch copyleft.
If you're a developer, this is really good for you, because it means that if a large corporation adapts your code to make it work for them and improves it, they have to share those improvements with you.
The Linux kernel is a great example of how copyleft has helped it grow to become the most-used operating system in the world. As devices adopt Linux (e.g., in Android phones), they are required to publish sources that make Linux compatible with their hardware, which is how the custom ROM community exists today!
Some corporations really hate copyleft because it forces them to open source their code, such as Google's [infamous policy on avoiding the AGPL](https://opensource.google/documentation/reference/using/agpl-policy/) like the plague. If that matters to you, you should use a more permissive license instead.
------
It's important to make sure that you are *allowed* to use a license in your project! After all, if you have a copyleft dependency, you can't license your program permissively. In addition, plenty of licenses are incompatible with one another, so you can't use both of them together.
In general, your program must be at least as copyleft as the most copyleft license in your dependency chain.
Although not a definitive guide, [https://choosealicense.com](https://choosealicense.com/) from GitHub is a great starting point to choose a license! So long as you're careful and check your dependencies' licenses, you'll be well on your way to contributing to the FOSS community!

View File

@ -0,0 +1,77 @@
---
title: "Git is a Blockchain"
date: "2022-10-02"
_draft: true
tags:
- tech
- albatross
---
This article is [also published in *The FOSS Albatross.*](https://medium.com/the-foss-albatross/git-is-a-blockchain-1060b53cea1f)
Git today is a beloved technology developers all around the world now use to track the history of a project through revisions. Obviously, it's not magic — if you look at the hidden files in a Git repository, you'll find a `.git` folder at least as big as the rest of your repo.
<!-- more -->
But for a technology that lets you revert the exact state of your files back any number of years, that `.git` folder is surprisingly small. You'd expect that any backup program worth its salt would actually back up each version of each file, but Git tracks *changes* to files instead of the actual files themselves, a method known as [delta encoding](https://en.wikipedia.org/wiki/Delta_encoding).
Not to mention all of the other features that Git piles on for practically no storage cost, such as branches, merges, cherry-picking, and more?
How does it do it, you ask?
Quantum Web3 decentralised machine learning VR blockchain agile AI cloud storage microservice architecture.
Only two out of the many buzzwords above are are actually used in Git, but Git was the decentralised blockchain before decentralised blockchains were cool.
But, I hear you cry, Git couldn't *possibly* be a blockchain! Blockchains are the stuff used in cryptocurrencies, scams, and other overhyped technologies!
In that case, why don't we compare the two? Let's start with the [Wikipedia definition of a blockchain:](https://en.wikipedia.org/wiki/Blockchain)
## A blockchain is a distributed ledger.
In simple terms, a blockchain isn't centralised, and a blockchain is copied across many computers, typically via peer-to-peer, such that each person with the data can verify that it's true.
…What do you know? That's exactly what Git does! Each Git repository is copied in its entirety by each person who clones it. When you push a commit, you send an update to another repo hosted somewhere else. When someone else pulls your commit, they *also* do it from a repo hosted somewhere else.
## A blockchain is decentralised.
At this point, Git servers have largely centralised into large services such as GitHub and GitLab, but that does not mean that the repository hosted there is the "true" one. Any contributor can overwrite the "centralised" version with their own local changes. In fact, there are lots of other server options out there from FOSS hosts such as Gitea, Codeberg, Sourcehut, and more. You can even host your own Git server!
Although Git *can* be centralised, the technology is inherently decentralised because it does not differentiate between servers — all of them are simple "remote"s.
## A blockchain consists of a growing list of records.
Transaction, meet commit. This one doesn't even need an explanation.
## Each record contains a cryptographic hash of the previous hash!
Hypothetically, right now, what if you run `git show` in your repository? Oh, hey! Is that a SHA-1 commit hash? Leading to the previous SHA-1 commit hash?? Who would've thunk it??? It's almost as if Git is a blockchain!!!!
```
commit ff25f8f352ed9e9f2fd07275ff136182a1711508
Author: NAME <EMAIL@EMAIL.COM>
Date: Tue Aug 16 22:45:12 2022 -0400
fix(api): add back imports
pylance keeps deleting them >:(
diff --git a/mandown/__init__.py b/mandown/__init__.py
index 4bba27f..9b12daa 100644
```
Each Git commit is hashed in a cryptographically secure* way that links back to the previous hash, which makes it so that you can't modify a previous hash without changing all of the hashes afterward.
This is literally what happens when you mess up a commit and try to revert it but the server yells at you due to mismatched histories because you messed up reverting it properly.
------
By every commonplace definition of the word, Git utilises a blockchain in its revision control system. It's at the heart of how each change can be uniquely tracked across systems.
A blockchain is nothing special. It's a technology you can use to avoid data tampering via communal tracking of transactions / commits. So, the next time you hear someone rambling on and on about blockchain and crypto, remember that it's just a tool as a means to an end, not some magical solution to everything.
------
*SHA-1 is no longer cryptographically secure, but Git has moved to a patched version that hasn't been cracked yet. SHA-256 support is also in the works.

View File

@ -0,0 +1,52 @@
---
title: "GitHub for Dummies"
date: "2022-06-17"
_draft: true
tags:
- tech
- albatross
---
This article is [also published in *The FOSS Albatross.*](https://medium.com/the-foss-albatross/github-for-dummies-1bb448962fc5)
Ever been linked to a GitHub page to download something and couldn't figure out what to click? This short, simple guide will help you get to wherever you need in 4 easy steps.
<!-- more -->
![What do all these words mean? Issues? Pull requests? Actions? Projects? Releases?](https://lh4.googleusercontent.com/u03FMDrVGVaU7x0ojxnNTAnM6_sPaSsnP1R6pZDJaTH95xqiH8LQKBN4OTZoU0Bigf6seLcCcDtSkuxcMwuLqLrCQH2fs6QsUZFyw58DN26sdbJcmMjXrhWjRQg6aoSzu-gBzib3gz20s0uFi_9h5k7QpgbohXqNkXw2pXPfPu4j3VibM_MNlHBK)
### The README and wiki
Always remember to first RTFM (Read The Fine Manual, with a slightly different word in place of "fine")! The README is always located on the home page of a repository, and it should contain a wealth of information for newcomers to familiarize themselves with the project. Installation instructions are often placed here if you scroll down far enough.
If that doesn't work, check out the project's wiki if they have one, located in the top navigation bar of the repository. Community members can also contribute to this page, so if you want to help the project and make it easier for newcomers, feel free to add or edit a page!
### Downloading releases
Now, if neither the README nor wiki have any hints to finding the link you want, you can go to the *Releases* section of the project, which on a desktop browser is in the right sidebar, while on mobile is located at the very bottom of the page.
![In this case, clicking one of the "primoprod" assets with the file extension for your device will get you a runnable program.](https://lh4.googleusercontent.com/v6G-c31NECe6ZJZhe2YSQXocQ4eCBJhYuXjNWSmECm5QQcSKaMWLpxe_roIkIonkMfUcDK4UtuqQEegVXCD1sAwHQnkssxOEk3uUrnQaMbhXL8zyeXdi0nUNv_QTKFsD5ZAUDJijHv_dc5wdTOEjggZipsIStM3vwaiabiNQ8XUY5bolApOupOwd)
Releases are the "official" way for projects to upload stable versions of their program to send to others. In the "Assets" section of a release, clicking the link that is not labeled "Source code" will get you a runnable version of the program.
### Downloading repository files
Let's say you don't want to download the whole program, but instead just a certain file *inside* the repository. What do you do?
Similar to Google Drive and other cloud storage platforms, you can click through different folders and eventually on files. Although you can't download folders, you *can* download individual files by clicking on them and then clicking "Raw" or "View Raw".
If that doesn't work, right-click the page that clicking "Raw" or "View Raw" opened and press "Save Page As".
### Filing issues
Trouble in paradise? If none of the above options worked, you can always file a new issue in the "Issues" tab of the project, where you can report bugs and ask questions directly to the project owner.
![](https://lh6.googleusercontent.com/l7mlo6OTPsAi17WcYgLeZ39aVp65D_24Kz4PMYOgKArwxJcz4jRcTCtud9UtChEUiUdnVR8sR7_6TvQJAAL2mFcKecLK-hhPvr7De_tPqrvh_mbaNCfVisD2yBn2icaXSl0eFDD4cIHKOzPKOM--2hruiM6qHkC6foW-6Pu63pU9c6FZOid10WXN)
Remember to read the project's issue guidelines, as some of them have a dedicated support forum or Discord to send help requests to.
### Contribute to FOSS!
For those just trying to get a file and dip, GitHub's interface is certainly more complex than it needs to be. The many buttons to streamline developers' experiences often get in the way of a user just trying to download the program.
As a developer, though, GitHub's many features are powerful and make development faster and more organised. If you ever decide to contribute to the FOSS community, return to GitHub and try to learn about the many other tools available to you!

View File

@ -0,0 +1,56 @@
---
title: "Google's Guide to Taking Over the Web"
date: "2022-07-16"
_draft: true
tags:
- tech
- albatross
---
This article is [also published in *The FOSS Albatross.*](https://medium.com/the-foss-albatross/googles-guide-to-taking-over-the-web-26847a389ac5)
Do you have a dream? A dream where you call the shots for billions of other people and how they communicate with each other? Well, we've got the guide of the decade for you, taken straight from personal experience. In just four easy steps, you'll be able to corner the tech industry yourself!
<!-- more -->
## 1. Bribe your competitors.
This first step is the most important. Without getting your name out there, you don't have the influence needed for web domination. Take out a small loan if you have to. We've bought out Mozilla for $450 million and Apple for $15 billion so that we're the default search engine in Firefox and Safari. Why would our users ever bother to change the default — to something like *Bing*, no less — when ours is good enough?
You gotta get your customers to associate you with the web, and you know you've got that when your company name is added to the English dictionary. What's the first thing you do whenever you want to look something up? You Google it. And the simplest, easiest, lowest-effort way to do this is by bribing your competitors.
## 2. Create a browser.
Make it simple. Make it fast. Make it such that people actually want to use it. Creating your own browser lets you into a whole bunch of important standards organizations, including Ecma and ISO, but most importantly, the W3C.
Now that everyone is using your search engine, you can abuse your near-monopoly there to tell everyone to use your new browser.
"A better way to surf the web."
And since you're Google, they trust you. After all, they get all your information from you, so why would you lie to them? Your users will all happily switch. And, if you've gotten creative, your newfound popularity might mean that your browser engine will be used *outside* of the browser in, say, a popular [server-side language](https://nodejs.org/en/), or even to [make desktop applications](https://www.electronjs.org/)!
All giving you more power over where the future of the web goes.
## 3. Extend open standards.
Now that you have a secure foothold in guiding the future of the web and everyone loves your products, it's time to take the rest of the pages out of Microsoft's playbook: Embrace, Extend, and Extinguish. It's time to add new features. Ones that your users like! But make sure you leverage your own services to show just how important you are to the web. Make others *depend* on you.
For example, you could [send all traffic to news sites through your own servers](https://en.wikipedia.org/wiki/Accelerated_Mobile_Pages) and call it "speeding up page loads." Don't forget to pretty it up! Open source it, but *you're* the only one allowed to contribute to it. In your search engine — you know, the one that everyone relies on? — rank sites higher depending on if they use your service. That'll encourage them.
Also, this point is a great time to start hobbling your browser competitors. Lots of people are using your own browser now, but the other browsers are just as good and less people are switching. That's a problem. What can you do about that?
Hypothetically, of course, you could take another one of your services — one might even say one that is indispensable to the web — and make it [several times slower in other browsers](https://fossbytes.com/youtube-slow-mozilla-firefox-edge/) by using special code in your own browser. Now you've got users' attention. An essential service they use is slow in their browser! How convenient, then, that your browser just happens to fix all those problems!
Alternatively, if you happen to own an operating system that [powers 70% of all smartphones](https://gs.statcounter.com/os-market-share/mobile/worldwide), you can also put it in there, as only [10% of users change their default mobile browser](https://www.independent.co.uk/tech/google-alternatives-privacy-duckduckgo-search-engine-browser-chrome-eu-fine-a8455321.html)!
## 4. Make so many standards until no one can catch up.
If you've made it this far, you've got this in the bag. Just make the web experience "more complete" and "more streamlined!" Do what you've been doing in step 3 but make it impossible for any newcomers to do it securely and properly. Add a protocol for [USB access in the browser](https://en.wikipedia.org/wiki/WebUSB). Add a protocol for [Bluetooth in the browser](https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API). Replace third-party cookies with your own specialized tracking mechanism. All in the name of security and privacy, of course.
There are so many "standard" features in Chrome that it is practically unsustainable to create a new browser engine properly. Opera gave up and switched to Chrome. *Microsoft* tried with Edge before they gave up and used Chrome's engine instead.
## 5. Profit!
That's it! No one can stop you now. You have a stranglehold on the web. Your browser / browser engine have [70% of browser share worldwide](https://gs.statcounter.com/os-market-share/mobile/worldwide). Even in China, where your whole ecosystem is practically banned, you have [more than 50% market share](https://gs.statcounter.com/browser-market-share/all/china). Developers optimize for you, further reducing the market share of other browsers as sites are now broken in other browsers.
You're done. You've taken over the web. There is nothing anyone can do…except switch to Firefox. But who's going to do that? Why would anyone sacrifice the convenience of Chrome just to reject anti-competitive behavior of one company?

View File

@ -0,0 +1,553 @@
---
title: "13 Tricks to Write Nicer Python"
date: "2022-08-21"
_draft: true
tags:
- tech
- albatross
---
This article is [also published in *The FOSS Albatross.*](https://medium.com/the-foss-albatross/13-tricks-to-write-nicer-python-d6c65897cd59)
Known as an easy-to-use and flexible programming language, Python nevertheless still has plenty of tricks you can use to make your code prettier and faster to write. No matter if youre new to Python or have years of experience, read more to find a tip for you!
<!-- more -->
## 1. F-strings
When printing out lots of text, you might find that you have to add lots of text together, which you might do by string concatenation:
```py
name = "John"
lastname = "Doe"
print("My name is " + name + " and my last name is " + lastname + ".")
```
As you can see, it gets quite long and cumbersome if the string is long enough and if youre adding enough strings. This is where **f-strings** come in to make the code readable and actually more performant! These special strings begin with the letter “f” immediately before the opening quote of a string, and any expression in curly braces will be evaluated.
```py
print(f"My name is {name} and my last name is {lastname}.")
```
Completely clear, right?
Thats not all they can do, either! F-strings also provide some ways to easily format the data by providing how you want the data to be displayed after a colon in the expression.
For example, you can limit the number of decimal places shown in a float to two decimal places…
```py
num = 123.456789
print(f"{num:.2f}")
```
…and even dates and times!
```py
from datetime import date
today = date(2022, 8, 21)
print(f"{today:%m/%d/%Y}")
```
Output:
```
08/21/2022
```
## 2. Using “if” to its maximum potential
Like any language, Python has its quirks with values that evaluate to `True` and `False` in if statements. Specifically, **only the following are False** and every other value is True.
- Zero (`0` and `0.0`)
- Empty containers, such as lists, tuples, dictionaries, sets, **and strings**
- `False`
- `None`
This means that actions like checking for empty containers can be drastically shortened from:
```py
array = []
if len(array) != 0:
print("something is in the list!")
```
to:
```py
array = []
if array:
print("something is in the list!")
```
## 3. List comprehensions
You can generate a new list very easily with an inline `for`.
```py
array = [i for i in range(10)]
print(array)
```
Output:
```py
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
```
List comprehensions are just syntactic sugar for a regular for loop, so the above code is equivalent to:
```py
array = []
for i in range(10):
array.append(i)
print(array)
```
Theyre also useful for performing operations *on* the elements, much like `map` in other functional programming languages.
```py
array = [i for i in range(10)]
new_array = [i ** 2 for i in array]
print(new_array)
```
Output:
```py
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
```
In addition, as an alternative to `filter`, you can only include specific elements by adding an `if` at the end:
```py
array = [i ** 2 for i in range(10) if i % 2 == 0]
print(array)
```
Output:
```py
[0, 4, 16, 36, 64]
```
This is equivalent to:
```py
array = []
for i in range(10):
if i % 2 == 0:
array.append(i ** 2)
print(array)
```
## 4. Slicing strings and arrays
Compared to more traditional languages that only allow array accesses `array[i]` for one element, Python lets you "slice" a list or any other sequence (like strings) by a colon between the two indexes.
```py
string = "hello world!"
array = [1, 2, 3, 4, 5, 6]
print(string[2:7])
print(array[:-2])
```
Output:
```
llo w
[1, 2, 3, 4]
```
## 5. Tuple expansion
Tuple expansion is one of Pythons most powerful features, allowing you to expand or assign multiple values in a single line.
```py
a, b = 1, 2
s1, s2 = "baguette", "tomato" + "cheese"
print(a, b)
print(s1, s2)
```
Output:
```
1 2
baguette tomatocheese
```
The code above effectively assigns a tuple `(a, b)` to another tuple `(1, 2)` and is equivalent to the code below:
```py
a = 1
b = 2
s1 = "baguette"
s2 = "tomato" + "cheese"
```
This can let you return multiple values from a function.
```py
def init():
return (5, 6)
a, b = init()
```
And its also really useful in swapping variables!
```py
a = 2
b = 4
# oops, assigned them wrong
b, a = a, b
```
Tuple expansion can even be used in for loops to iterate over tuples or dictionaries!
```py
items = {
"apple": "juice",
"orange": "pulp",
"banana": "smoothie",
"mango": "slushie"
}
for key, value in items.items():
print(key, value)
```
Output:
```
apple juice
banana smoothie
mango slushie
orange pulp
```
## 6. Shorter code with shortcircuiting
Imagine you want to assign a variable based on the value of another variable. You might do it this way:
```py
name = "John"
if name == "John":
last_name = "Doe"
else:
last_name = "Unknown"
```
But thats long, and `last_name` appears twice. You can have an inline if to do it in just one line:
```py
name = "John"
last_name = "Doe" if name == "John" else "Unknown"
```
Much nicer!
You also might be familiar with logical boolean operators and how theyre used in if statements.
```py
if password == "hunter2" and name == "Joe":
print("password accepted")
```
But you might not know some of their quirks or exactly how they work.
If you have two things opposite an `and`, for example `x and y`:
- If `x` evaluates to True, `y` is returned.
- If `x` evaluates to False, `x` is returned.
This might not seem that useful, but then look at `or` in `x or y`:
- If `x` evaluates to True, `x` is returned.
- If `x` evaluates to False, `y` is returned.
This might also not seem that useful until you learn that logical operators can also be used in assignments, where theyre most commonly used for fallback values.
```py
confirm = input("Accept the EULA? (Y/n) ") or "y"
```
Because `input` returns a string, if the user doesn't enter anything, it evaluates to False, so the value `"y"` is assigned to `confirm`.
## 7. Safer file handing with context managers
In most languages, when you write something to a file, its actually held in a buffer until you close it manually or automatically when the program ends.
In Python, you would run the following to write to a file:
```py
file = open("myfile.txt")
file.write("hello file")
file.close()
```
But thats a lot of lines, and you might forget to run `file.close()`, particularly in long programs with lots of stuff being written to the file.
Thats where **context managers** come in, abstracting the whole process and calling `file.close()` automagically:
```py
with open("myfile.txt") as file:
file.write("hello file")
```
## 8. Nicer iteration with zip() and enumerate()
Pythons for loop is commonly known in other programming languages as a for-each loop. This is great if you just want each item in an iterable, but sometimes you want the index too! Instead of having to resort to `range(len(array))`, instead you can use `enumerate()` and tuple expansion to easily get both the index of the element and the element itself:
```py
array = ["a", "b", "c", "d", "e"]
for i, c in enumerate(array):
print(i, c)
```
Output:
```
0 a
1 b
2 c
3 d
4 e
```
In a similar vein, if you have two arrays you want to process at the same time, you dont need to use `range(len(array))` when you have `zip()`, which will bundle the different iterators into one big one as big as the smallest iterable.
```py
ints = [1, 2, 3, 4]
strs = ("pomme", "poutine", "pinterest", "pear")
floats = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
for i, s, f in zip(ints, strs, floats):
print(i, s, f)
```
Output:
```
1 pomme 1.0
2 poutine 2.0
3 pinterest 3.0
4 pear 4.0
```
## 9. Nicer paths with pathlib
Python is a cross-platform language, and if theres one annoying difference between Windows and macOS / Linux, its that their path separators are different: Windows uses the backslash (`\`) while macOS and Linux use the forward slash (`/`), and attempting to access a path the wrong way will result in file not found.
So, in the olden days, you would have to string together a long string with `os.path.join` just to be safe:
```py
import os
path = os.path.join("folder", os.path.join("subfolder", "subsubfile.txt"))
```
Doesnt that look clunky?
Luckily, a “new” (read: years-old) [addition to the standard library](https://docs.python.org/3/library/pathlib.html) makes that *much* easier:
```py
from pathlib import Path
path = Path("folder") / "subfolder" / "subsubfile.txt"
```
It also includes a bunch of helper methods to check for things and navigate the filesystem tree just because Python loves making your life easier.
```py
from pathlib import Path
path = Path("folder")
if path.exists() and path.is_file():
print("yay!")
print(path.parent)
print(path.suffix)
if not path.exists():
path.mkdir() ### create the folder if it doesn't exist
```
## 10. Iterable unpacking
n competitive programming, often you have to print out space-separated results. This can be done by the mildly inconveniencing
```py
print(" ".join([1, 2, 3, 4]))
```
or heaven forbid, via iteration:
```py
for i in [1, 2, 3, 4]:
print(i, end=" ")
print()
```
which is where iterable unpacking comes in, and you can go straight to
```py
array = [1, 2, 3, 4]
print(*array)
```
The **unpacking operator** (the asterisk) basically gets rid of the container and throws all of the elements inside directly into the print function as parameters.
That means that the above line of code is equivalent to:
```py
print(1, 2, 3, 4)
```
which nicely prints out the integers separated by spaces with a newline at the end.
But wait, theres more! The unpacking operator is also commonly used in function definitions as a catch-all parameter for extra arguments, stuffing them into a list.
```py
def init(a, b, *args):
print(a, b)
print(args)
init(1, 2, "pomme", 4, 6.0)
```
Output:
```
1, 2
['pomme', 4, 6.0]
```
You can also use this in normal assignment to, say, only get the first and last elements of an array:
```py
first, *args, last = [1, 2, 3, 4, 5]
print(first)
print(args)
print(last)
```
Output:
```
1
[2, 3, 4]
5
```
## 11. Else outside of if
Everyone knows what if-else does. But did you know that Python also lets you use it after loops and exception blocks?
- In loops, `else` is run *only if* the loop did not `break`.
- In exception blocks, `else` is run *only if* there was no exception.
These are especially useful so you dont have to add an indicator variable yourself:
```py
for i in array:
if i.is_tomato():
print("found a tomato!")
break
else:
print("no tomato found :(")
```
…is equivalent to
```py
found_tomato = False
for i in array:
if i.is_tomato():
found_tomato = True
break
if found_tomato:
print("found a tomato!")
else:
print("non tomato found :(")
```
Similarly, in a try-except block:
```py
try:
number = int(input("Enter a number: "))
except ValueError:
print("That's not a number >:(")
else:
print("You know how to follow instructions! :D")
```
…which is equivalent to:
```py
is_number = True
try:
number = int(input("Enter a number: "))
except ValueError:
is_number = False
if is_number:
print("You know how to follow instructions! :D")
else:
print("That's not a number >:(")
```
## 12. Type hinting
When you use a library, your IDE often knows what types a function will accept so you dont have to guess.
These come from **type hints** in the code, which you can use in your own code, especially if there is a lot of reused code and its a long program.
A colon after a variable shows its type:
```py
a: int = 4
```
…while an arrow after a function shows its return value.
```py
def pow(x: int, y: int) -> int:
return x ** 2
def largest(array: list[int]) -> int:
return max(array)
```
In more complex programs, type hinting is especially useful as your IDE can provide autocomplete and better syntax highlighting as it knows the limits of your program, so if you accidentally assume the wrong type of an object, your IDE will complain and find the bug before it causes a runtime crash.
## 13. The walrus operator (:=)
Say youre doing something over and over again and checking for a condition until its true. You could do it in a while loop:
```py
import requests
r = requests.get("https://google.com")
while r.status_code != 200:
print(r.status_code)
time.sleep(1)
r = requests.get("https://google.com")
```
But the assignment is repeated twice!
Introducing the walrus operator, which assigns *and returns the assigned value* in the same statement, allowing for less repeated code and some pretty crazy one-liners:
```py
import requests
while (r := requests.get("https://google.com")).status_code != 200:
print(r.status_code)
time.sleep(1)
```
---
Although thirteen of them are covered here, there are endless ways to optimise your code so that its faster to read, write, and run. Python is a “batteries included” language — chances are that the way youre used to doing things in other languages have a shorter and more concise method in Python.
The most important tip I can give you is to check the standard library if you want to do something — from [image format recognition](https://docs.python.org/3/library/imghdr.html) to [config file management](https://docs.python.org/3/library/configparser.html) to [basic database operations](https://docs.python.org/3/library/sqlite3.html), the standard library is chock full of useful tools included over the years.

View File

@ -0,0 +1,55 @@
---
title: "Reviving Older Games Through Emulation"
date: "2022-05-22"
_draft: true
tags:
- tech
- albatross
---
This article is [also published in *The FOSS Albatross.*](https://medium.com/the-foss-albatross/reviving-older-games-through-emulation-ca4e9705700c)
Throughout the past few decades, video games have evolved from an art medium restricted solely to specialized consoles to one enjoyable on all sorts of platforms, including everyday devices such as PCs and phones, replayable for the years to come.
However, for all of the beloved games that *are* locked to consoles, what option do you have but to let them die out as discontinued second-hand consoles grow more and more expensive as they break down?
Introducing…emulation!
<!-- more -->
### TIL converting games to Australian birds will preserve them.
![(Wikipedia Commons, public domain)](https://upload.wikimedia.org/wikipedia/commons/9/9d/Emu_1_-_Tidbinbilla.jpg)
Though *emu* and *emulation* share three letters, the only other similarity they have is that large entities have tried and failed miserably to stamp them out in the past. Emulation is the process of one system (such as a phone or computer) imitating another one (such as a video game console) to run programs designed for that system. Emus will now forever ravage the Australian wilderness, and emulation has been ruled to be legal in at least the United States.
Today, almost every popular system with games in demand has an emulator developed for them that works to some extent, allowing people to take their *rightfully owned* games and play them long after the original system has been discontinued or play them with mods to enhance their experience such as by increasing resolution.
In a nutshell, this means that if the Nintendo DS you owned as a kid suddenly broke one day, you can back up your cartridges and play Pokémon Black on your phone or computer instead!
### Cool, how do I try it out?
Modern-day emulators are nearly all FOSS, allowing them to be ported to most desktop and even mobile operating systems. We recommend the emulators below for their respective systems for a variety of reasons, including user-friendliness, ease of use, performance, and accessibility.
| Platform | PC | Android |
| ----------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| Sony PlayStation 2 | [PCSX2](https://pcsx2.net/) | [AetherSX2](https://www.aethersx2.com/) |
| Nintendo GameCube / Wii | [Dolphin](https://dolphin-emu.org/) | [Dolphin](https://dolphin-emu.org/) |
| Nintendo DS | [melonDS](https://melonds.kuribo64.net/) / [DeSmuME](https://desmume.org/download/) | [DraStic](https://play.google.com/store/apps/details?id=com.dsemu.drastic&gl=US) / [melonDS](https://github.com/rafaelvcaetano/melonDS-android) |
| Sony PlayStation 3 | [RPCS3](https://rpcs3.net/) | None |
| Nintendo 3DS | [Citra](https://citra-emu.org/) | [Citra](https://citra-emu.org/) / [Mikage](https://mikage.app/) |
| Nintendo Switch | [yuzu](https://yuzu-emu.org/) / [Ryujinx](https://ryujinx.org/) | [Skyline](https://github.com/skyline-emu/skyline) (WIP) |
Do note that emulators, unlike typical games, are *much* heavier on the CPU compared to the GPU because of all the translation required for the emulator to "convert" the game from the console's "language" to that of your phone or PC.
You can find more details about each emulator and other options for different consoles on the [Emulation General Wiki](https://emulation.gametechwiki.com/index.php/Main_Page).
### I want to learn more!
From stories about games relying on undocumented behaviour to *game-breaking* bugs cancelled out only by *console-breaking* bugs, emulation is a fascinating topic well-deserving of your interest if you're at all interested in low-level systems.
Modern emulators usually perform high-level emulation (HLE), where they emulate the functions that the system OS provides, compared to emulating each transistor like some monstrously complex Minecraft redstone contraption. As you might guess, this approach sacrifices some accuracy for greater performance — it's like translating a book character by character instead of word by word.
For further reading, there are plenty of blogs run by emulators outlining their tales in overcoming tricky obstacles, and we here at the *Albatross* strongly recommend you go check them out! [Dolphin](https://dolphin-emu.org/blog/) (GameCube / Wii), [yuzu](https://yuzu-emu.org/entry/) (Switch), [Ryujinx](https://blog.ryujinx.org/) (Switch), and [RPCS3](https://rpcs3.net/blog/) (PS3) all attempt to post regular progress reports on their respective blogs, linked above.

View File

@ -0,0 +1,34 @@
---
title: "Appreciate Your Browser!"
date: "2022-09-18"
_draft: true
tags:
- tech
- albatross
---
This article is [also published in *The FOSS Albatross.*](https://medium.com/the-foss-albatross/appreciate-your-browser-82d36a81a696)
*Click.* Milliseconds after you press a link, your screen flashes white. Elements slowly load in as a page full of search results appears in front of your eyes. How does this magic work? How does your computer know exactly what to show you when you press a button on your mouse? Let's go on a journey through your system to see exactly what happens.
<!-- more -->
------
The instant your finger pushes past a physical barrier, a switch connects a circuit and electrons go speeding through a wire (or over the air if your mouse is wireless) to your computer.
The moment the central processing unit (CPU) of your computer receives the electrons, it propagates that signal up to your operating system as a **hardware interrupt**, pulling all the brakes on whatever it's currently doing to handle the signal.
Eventually, your browser realises it's been clicked on and starts the process to fetch the page you're looking for from the internet. Through the operating system, your browser sends a small packet of information to your router, asking it where exactly https://en.wikipedia.org is through its **domain name system** (DNS)!
Your router kindly asks several other routers, which in turn ask even more routers until it gets back to you with a "208.80.154.224"!
Armed with this **internet protocol** (IP) address, your browser then asks your router to connect to it, which it happily does, informing you when it returns that the server with that address does in fact exist and is alive.
Now it's finally time to connect to the server. Your browser extends a handshake to the server, hoping it will reciprocate. Because Wikipedia is a trustworthy site that knows social expectations, it will return the handshake, whispering a secret only it knows through the **Transport Layer Security** (TLS) protocol.
Assured of Wikipedia's trustworthiness, your browser whispers secrets back, telling Wikipedia the information you're looking for. Wikipedia nods knowingly, but because they have other people to meet, they tell you everything you could ever need to know about parakeets in less than a millisecond with the power of **gzip** (compression) — you just need some time to decipher it.
Finally satisfied, your browser arranges the data prettily so that it's easy for you to read and holds it out to you expectantly, waiting for your praise.
Unfortunately, you snatch it out of your browser's hands, berating it for being so slow as you skim only the opening paragraph before yelling at it to fetch more information from Wikipedia, this time about parrots.

View File

@ -0,0 +1,9 @@
---
title: "Politics of Barin"
date: 2022-10-24
_draft: true
tags:
- barin
---

View File

@ -4,6 +4,7 @@ date: "2022-01-15"
tags: tags:
- tech - tech
- primoprod - primoprod
- featured
--- ---
Six months have passed since the [first progress report](/blog/2021/primoprod-progress-report/). Since then, a flood of changes have made it in, including hundredth-class Android support! Six months have passed since the [first progress report](/blog/2021/primoprod-progress-report/). Since then, a flood of changes have made it in, including hundredth-class Android support!
@ -14,8 +15,7 @@ This report will cover from where the previous left off to the present day: 21 A
## No more ## No more
::image{src="primoprod-itemrevealscreen.png"} ![](primoprod-itemrevealscreen.webp)
::
It's done. The pull screen is done. The element/weapon icon was added to the pull. Audio syncs up (well enough). The only thing missing is all of the fancy effects like glow and particles. It's done. The pull screen is done. The element/weapon icon was added to the pull. Audio syncs up (well enough). The only thing missing is all of the fancy effects like glow and particles.
@ -23,8 +23,7 @@ Nah, this is good enough.
## Take this! ## Take this!
::image{src="primoprod-questscreen.png"} ![](primoprod-questscreen.webp)
::
Until now, you had to manually edit the browser's `localStorage` to gain any currency. The quest screen makes primoprod finally usable as now you can make your own long-term "quests" that give 900 Primogems each as well as set four daily "tasks" that give 30 Primogems each plus 60 when all are done — if only the base game was this generous. These are editable and can have whatever title or description you want. The logic here went through several rewrites as the structure was finalised and an interface developed to the rest of primoprod. Dailies will automatically refresh themselves on the next day. Until now, you had to manually edit the browser's `localStorage` to gain any currency. The quest screen makes primoprod finally usable as now you can make your own long-term "quests" that give 900 Primogems each as well as set four daily "tasks" that give 30 Primogems each plus 60 when all are done — if only the base game was this generous. These are editable and can have whatever title or description you want. The logic here went through several rewrites as the structure was finalised and an interface developed to the rest of primoprod. Dailies will automatically refresh themselves on the next day.
@ -56,8 +55,7 @@ Sounds incredible, right? As it turns out, as you gain more experience with tech
This is why [the shop](https://github.com/potatoeggy/primoprod/blob/master/src/components/ShopScreen.vue) and the [dialog to buy things from the shop](https://github.com/potatoeggy/primoprod/blob/master/src/components/ItemPurchaseOverlay.vue) are so nicely done! It reused most of my types and was admittedly much simpler than some of the other screens, but I only ran into one insurmountable problem: range styling. This is why [the shop](https://github.com/potatoeggy/primoprod/blob/master/src/components/ShopScreen.vue) and the [dialog to buy things from the shop](https://github.com/potatoeggy/primoprod/blob/master/src/components/ItemPurchaseOverlay.vue) are so nicely done! It reused most of my types and was admittedly much simpler than some of the other screens, but I only ran into one insurmountable problem: range styling.
::image{src="primoprod-itempurchaseoverlay.png"} ![](primoprod-itempurchaseoverlay.webp)
::
*I wish I was actually this rich in the base game.* *I wish I was actually this rich in the base game.*
@ -67,8 +65,7 @@ As you can see, the slider looks very out of place. Why? That's because [only Fi
Up until now, only one banner was supported. This was finally fixed in November with the addition of [banner headers](https://github.com/potatoeggy/primoprod/pull/25) to match the base game. Now, you can simultaneously roll for Qiqi on *both* banners! Up until now, only one banner was supported. This was finally fixed in November with the addition of [banner headers](https://github.com/potatoeggy/primoprod/pull/25) to match the base game. Now, you can simultaneously roll for Qiqi on *both* banners!
::image{src="selected-wanderlust-invocation.webp"} ![](selected-wanderlust-invocation.webp)
::
On a side note, did you know that Vue puts all of their reactive things into Proxies? This means you can't simply `console.log(obj)` without going through five more clicks to find what you actually want. No, no. To properly print out the actual object, you have to *copy its contents to a clean, non-reactive Object* for this to work. Why?? On a side note, did you know that Vue puts all of their reactive things into Proxies? This means you can't simply `console.log(obj)` without going through five more clicks to find what you actually want. No, no. To properly print out the actual object, you have to *copy its contents to a clean, non-reactive Object* for this to work. Why??
@ -127,8 +124,7 @@ Back to laptop compilation we go!
Obviously, you aren't a *real* free and open source project if you don't have [pretty badges](https://github.com/potatoeggy/primoprod/blob/master/README.md) on your README. This was a major concern, so I copied Vue's style and now made primoprod a proper FOSS repo — build checkmark and badges and all! Obviously, you aren't a *real* free and open source project if you don't have [pretty badges](https://github.com/potatoeggy/primoprod/blob/master/README.md) on your README. This was a major concern, so I copied Vue's style and now made primoprod a proper FOSS repo — build checkmark and badges and all!
::image{src="primoprod-badges.png"} ![](primoprod-badges.webp)
::
## Kids and their phones ## Kids and their phones
@ -140,8 +136,7 @@ ItemRevealScreen? What's that? Now [WishBanners](https://github.com/potatoeggy/p
At last, though, we have a [proper mobile UI](https://github.com/potatoeggy/primoprod/pull/33). At last, though, we have a [proper mobile UI](https://github.com/potatoeggy/primoprod/pull/33).
::image{src="mobile-primoprod.png"} ![](mobile-primoprod.webp)
::
Still some niggles to work out, but it looks "good enough"! With the completion of proper mobile orientation came the merging of the [Android branch](https://github.com/potatoeggy/primoprod/pull/32) made with [Capacitor.js](https://capacitorjs.com/), basically the mobile equivalent of Electron. It has even more niggles than the web version does. Still some niggles to work out, but it looks "good enough"! With the completion of proper mobile orientation came the merging of the [Android branch](https://github.com/potatoeggy/primoprod/pull/32) made with [Capacitor.js](https://capacitorjs.com/), basically the mobile equivalent of Electron. It has even more niggles than the web version does.
@ -151,6 +146,6 @@ If you have any idea how to fix them, please do send a suggestion on the [issue
And that's everything that changed since August! Primoprod has come a long way from being a mere personal project to a proper personal project with fancy badges! Hopefully in the future it'll be able to be played standalone (chibi sprite combat, anyone?) so that it'll be interesting enough that people can set their goals and stick to them. And that's everything that changed since August! Primoprod has come a long way from being a mere personal project to a proper personal project with fancy badges! Hopefully in the future it'll be able to be played standalone (chibi sprite combat, anyone?) so that it'll be interesting enough that people can set their goals and stick to them.
In case you'd like to check it out, version 1.1.0 released just [yesterday](https://github.com/potatoeggy/primoprod/releases/tag/v1.1.0) and is available for download for Windows, macOS, Linux, and Android, with a web version available at https://primoprod.eggworld.tk! In case you'd like to check it out, version 1.1.0 released just [yesterday](https://github.com/potatoeggy/primoprod/releases/tag/v1.1.0) and is available for download for Windows, macOS, Linux, and Android, with a web version available at https://primoprod.eggworld.me!
Until next time! Until next time!

View File

@ -0,0 +1,102 @@
---
title: "Rust Changes How You Think And Code"
date: 2022-11-27
_draft: true
tags:
- tech
- rust
- albatross
---
This article is [also published in *The FOSS Albatross.*](https://medium.com/the-foss-albatross/rust-changes-how-you-think-and-code-2b5ee4d8def2)
Rust is the hot new language on the block (as new as a language from 2006 can be) that boasts reliability and efficiency.
How does it do this? Well, Rust has something that no other language does — it guarantees memory and thread safety while maintaining the same high performance of C or C++, all the while having high level features such as pattern matching and functional programming!
<!-- more -->
Some languages come close: Go is known for being both fast to run and to write, but its garbage collector and xenophobia toward other languages adds overhead that means that it is not suited for a systems programming language.
In safe Rust, there is *no such thing* as undefined behaviour. Everything your code says it does will happen — segfaults and NullPointerExceptions are impossible.
For simpler issues, the rustc compiler tells you more or less exactly what went wrong, along with a helpful error code, a link for examples on how to fix the error code, and even a suggestion that applies directly to your current code, which more times than not immediately fixes the issue.
```rust
error: format argument must be a string literal
--> helloworld.rs:3:14
|
3 | println!(123);
| ^^^
|
help: you might be missing a string literal to format with
|
3 | println!("{}", 123);
|
error[E0384]: cannot assign twice to immutable variable `a`
--> helloworld.rs:3:5
|
2 | let a = 123;
| -
| |
| first assignment to `a`
| help: consider making this binding mutable: `mut a`
3 | a *= 2;
| ^^^^^^ cannot assign twice to immutable variable
```
Thanks, rustc!
In time, you come to stop thinking less about the edges in the language and focus more on implementing what you want to implement. Rust makes it so that you don't have to stop and ask yourself these questions every minute:
- What if this variable isn't initialised or defined?
- What if this variable is already used?
- What if I'm modifying a variable that isn't supposed to be modified?
- What if another thread changes this data while I'm reading it?
- Did I forget to handle an error?
- Did I forget to check the error?
So how does it do this?
## Immutability by default
There are debates on whether immutable or mutable objects are better. Well, Rust provides both — but you have to *explicitly* tell Rust that you want your variables to be mutable. For example, the second error message in this article shows that you need the `mut` keyword to let the compiler let you change variable values.
```rust
let mut a = 1;
```
This applies to everything: from references to function arguments. If a variable isn't passed as `mut`, it's not mutable, and there is nothing else you can do to get around that. This isn't like JavaScript's `const`, either — the internal variables of a struct also have to be declared mutable in order to overwrite them.
This added friction to mutability means that developers tend to prefer immutable objects when possible, so it's very clear when a variable can change!
## The borrow checker
Perhaps Rust's flagship feature, this is how Rust manages memory without the complexity of manual memory management or the overhead of a garbage collector. In a nutshell, each variable is given an owner, and they may only have one owner.
You can "borrow" the value if you want to do something with it but give it back to use later, but the typical pitfalls of pointers don't exist in Rust because *there are no pointers in Rust!* (At least, not safe Rust.)
Here's an example of what Rust prevents — if you operated on vector B, it would change C, so Rust's safety guarantees would not hold. That's why the compiler doesn't let you run this in the first place.
```rust
let a = vec![1, 2, 3];
let b = a;
let c = a;
2 | let a = vec![1, 2, 3];
| - move occurs because `a` has type `Vec<i32>`, which does not implement the `Copy` trait
3 | let b = a;
| - value moved here
4 | let c = a;
| ^ value used here after move
```
And it's here that you really have to appreciate how much information the compiler gives you. It:
- tells you where the value originated from
- tells you where the value was used
- and tells you where the value was used *again*, which is not allowed
## Conclusion
Though there is a rather steep learning curve from just *how much* there is to unlearn about the finnicky things you can do in more traditional languages, Rust is a language that lets the computer calculate if your program is correct, letting you think purely on how to solve your problems.

View File

@ -0,0 +1,38 @@
---
title: "What's Wayland? Linux's \"New\" Display Server"
date: 2022-12-11
_draft: true
tags:
- tech
- albatross
---
Wayland is the shiny next-generation display server protocol that finally lets the Linux desktop move away from the cludgy abomination that is X. Although it was initially released in 2008, it's only recently that it has matured enough to the point that Linux distributions have begun to default to it.
<!-- more -->
## History
Ever since 1984, the Linux desktop has used the X Window System in order to output contents to a display.
38 years later, it's still alive and kicking in most Linux computers with all of its outdated features, such as slow compositing, built-in easy keylogging, and a lot of overhead just in case you want to operate your computer over the internet! Even with piled on extension (xrandr) after extension (xrender) and extension (composite), all of the bloat accumulated over time to the point that most of the X.Org server team moved to work on Wayland. As of today, X.Org is in maintenance mode, with few contributions and even fewer contributors because of all of the legacy code and practices that have to be worked with.
And that's exactly why Wayland was created — to be a simple, modern, and fast display protocol that narrows its scope to focus entirely on displaying windows efficiently and securely. Secondary priorities such as remote desktop, screen sharing, and screen tearing were implemented much later on.
No longer can any window catch every keypress without asking the user! No longer does shutting down the compositor crash nearly every program! No longer can any application decide to take over your screen and wreak havoc because it's allowed to do *literally anything!* No longer is workaround after workaround needed to maintain a *research project* that was never meant to be globally deployed!
Not to mention that because Wayland was designed from the ground up, it supports modern display technologies that X simply isn't able to due to technical limitations, such as different variable refresh rates over different displays at different resolutions.
## Adoption
Both Firefox and Chromium natively support Wayland. The GTK+, Qt, Electron, and SDL toolkits do too, letting thousands of applications "just work" by updating their dependencies.
In fact, the biggest desktop environments already support Wayland and all of its benefits:
- GNOME and KDE Plasma both have great support and even default to it on some distros.
- [Sway](https://swaywm.org) is a drop-in replacement for the i3 window manager if you're into tiling WMs.
- The Steam Deck uses Wayland to contain and sandbox games in its Gamescope compositor.
In addition, even for legacy apps such as games that can never be updated to support Wayland, a built-in compatibility layer called XWayland is there to run X applications in their very own contained X server. Even so, limitations in XWayland prevent it from being a one-size-fits-all solution to every X application.
Regardless, Wayland is still the future! Development on X has more or less completely stopped, so unless a party is desperate for a feature and is willing to invest a lot of time and money into navigating around the clunkier parts of the X.Org codebase, Wayland is the only available option for the future.

View File

@ -0,0 +1,120 @@
---
title: "Why Use Web Frameworks?"
date: 2022-10-30
_draft: true
tags:
- tech
- albatross
---
This article is [also published in *The FOSS Albatross*](https://medium.com/the-foss-albatross/why-use-a-web-framework-e1bdf1a8c1cf).
You're a web developer and you need to make a website. How should you build one? Do you start writing static files right away to be served immediately, or do you start setting up a project environment?
<!-- more -->
First, do you decide to use a framework? They're all the rage these days.
By far the biggest advantage of a web framework is its ability to make things declarative. Imagine you want to make a to-do list. You might write code like this to be able to add a new todo:
```html
<script>
function newTodo() {
const divBox = document.createElement("div")
const input = document.createElement("input")
input.type = "checkbox"
const p = document.createElement("p")
p.appendChild(document.createTextNode("Untitled todo"))
divBox.appendChild(input)
divBox.appendChild(p)
document.getElementById("button-box").appendChild(divBox)
}
</script>
<div id="button-box">
<div>
<input type="checkbox" />
<p>Do chores</p>
</div>
<button onclick="newTodo">New to-do</button>
</div>
```
...but then how would you make the whole system interactive? How could you delete todos, edit todos, or mark them as completed? You'd have to bring a whole state system that updates the document just to manage a simple todo app!
Meanwhile, frameworks abstract a lot of the element creation away so that you can focus on just writing components and how they *should* behave, not how to implement their behaviour.
For example, in Vue.js, you could do something like this:
```vue
<script setup>
import { ref } from "vue";
const items = ref([
{ title: "Do chores", completed: false }
]);
function newButton() {
items.push({ title: "Untitled to-do", completed: false );
}
function deleteButton(index) {
items.splice(index, 1);
}
</script>
<template>
<div>
<div v-for="(item, i) in items" :key="i">
<input type="checkbox" @click="item.completed = !item.completed" />
<p>{{ item.title }}</p>
<button @click="deleteButton(i)">Delete</button>
</div>
<button @click="newButton()">New to-do</button>
</div>
</template>
```
This short snippet makes it clear to the developer exactly what's happening and does way more than the pure version above. Not only can it add new todos, it can delete them and check them off too! At the cost of learning just a little more syntax, you can make the framework do much of the heavy lifting for you.
In this example, you tell Vue that you want a `div` for every item in your array with a checkbox, paragraph, and button. Whenever you add or remove an item to that array, Vue is responsible for making sure that your website matches the updated array so you don't have to fiddle with the document.
## Components
Working on files with hundreds of lines is never fun. Since HTML can only be sent in one file, this means that complex client-side applications tend to have too many lines to properly read and understand if they don't use a framework.
Once again, frameworks come to the rescue! Most of them have a component model that lets you reuse a lot of code, making it easier to read.
For example, once again in Vue:
```vue
<template>
<div>
<p>Hello</p>
<p>This is going to be used a lot!</p>
</div>
</template>
```
```vue
<script setup>
import UsedALot from "UsedALot.vue";
</script>
<template>
<UsedALot />
<UsedALot />
<UsedALot />
</template>
```
This splitting of logic makes it easier for your brain to focus on specific subchunks of code so it's easier to read and understand.
---
Web frameworks offer a lot of advantages over writing in a more imperative style. However, their abstractions do come at a cost — an extra minimum [16 KB](https://vuejs.org/about/faq.html#is-vue-lightweight) in JavaScript has to be fetched from your server, which can increase bandwidth costs and make your site slower.
Ultimately, in creating your website, much like when you do anything else, you should weigh the pros and cons of web frameworks before deciding to go with one or the other.

64
content/blog/2022/wine.md Normal file
View File

@ -0,0 +1,64 @@
---
title: "Running Windows Apps on Mac and Linux"
date: "2022-10-16"
_draft: true
tags:
- tech
- albatross
---
This article is [also published in The FOSS Albatross.](https://medium.com/the-foss-albatross/running-windows-apps-on-mac-and-linux-c372996588af)
Windows is a beloved operating system with absolutely no flaws whatsoever. It is the pinnacle of engineering — sheer perfection in its design.
<!-- more -->
But say you like being objectively correct and actually think that Windows has its own problems, so you decide to switch to a Mac or to use Linux instead.
What happens to all of the programs you leave behind? Linux and macOS don't recognise .exe files as ones they can run, so you won't be able to run those incompatible with Linux without extra work. If you can find alternatives for all of them, that's great, but there still might be stragglers for beloved programs such as niche apps and games.
You could launch real Windows inside a virtual machine, which guarantees compatibility (except with certain anti-cheats), but tends to have a pretty big performance impact. If you're interested, check out [our article on running virtual machines](https://medium.com/the-foss-albatross/an-os-inside-an-os-how-to-run-virtual-machines-a3ddf6c8bbed)!
In this article, we'll be looking at Wine, a program that is practically magical by letting you run Windows apps natively on macOS or Linux!
## What is Wine?
Wine is a common alcoholic beverage —
Wine is a *compatibility layer* for Windows applications, translating Windows system calls into Mac or Linux ones.
Windows programs are actually quite similar to Linux and macOS programs. They all make calls to system APIs, and they all compile down to machine code. Because the different OSes run on the same x86_64 architecture (pre-Apple M1), there's no need to go through the expensive emulation of a whole other CPU like you see in game console emulators.
To be able to run the program, Wine hooks into the application and intercepts all of the calls it makes, passing it to its own reimplementation of the Windows filesystem and its various libraries or DLLs.
In fact, some of the APIs called are actually the same on all operating systems (such as OpenGL for graphics), so Wine can pass those through directly. In some cases, running an application through Wine can be even faster than on Windows!
Even if the APIs aren't exactly the same, Wine can still do a little bit of work to make them compatible. For example, plenty of work in making games run better has led to DirectX 12, 11, and 9 all translatable to the lower-level Vulkan, supported natively on Linux. On macOS, yet another translation layer takes the Vulkan output and turns it into the macOS-preferred API, Metal. Ironically, some games on Windows run *faster on Windows* if you run a DirectX-to-Vulkan translator! Combined with the general impression of a faster Linux, you can see how games can run *faster* under Wine than in Windows!
Wine's integration and familiarity with the host (Linux or macOS) means that it has some niceties that make it easier to use than a virtual machine, such as desktop integration! Shortcuts from installed Windows apps will appear on your desktop and in your launcher, and you can access all your files from within Wine. Pre-established shared Documents / Desktop / Videos / Music folders mean that getting started is super easy.
## Does it work?
Anyone who's tried to run a modern version of Office will be able to tell you that compatibility isn't perfect. Although Windows APIs are documented by Microsoft, many applications rely on undefined behaviour and quirks in Windows' implementation of those APIs, which Wine has to chase down and patch each time.
In addition, some anti-cheats fundamentally rely on the Windows kernel, and Wine's reimplementation isn't yet complete. Valve's work on the Steam Deck, which uses Wine extensively under the hood to run games on its Linux platform, has helped substantially in persuading developers to support Wine, but still many other games remain locked to Windows under the guise of fairness.
In general, howeve
r, you can expect many applications to at least start. Wine themselves [maintains a database](https://appdb.winehq.org/) of how well many applications run on their website. If it doesnt work—it was worth a shot, at least!
## How can I use it?
Wine is already in many Linux distributions' repositories, so installing it through your package manager should get you up and ready.
On macOS (pre-10.15), Wine can be installed using homebrew [as described on their website](https://wiki.winehq.org/MacOS).
Once Wine is installed, running a program is as easy as passing the path of the program you want to run into Wine:
```
wine path/to/program.exe
```
…or more commonly, you can just double-click the .exe file.
The decades spent on making the transition away from Windows easier have resulted in a marvel of a project capable of running proprietary software on a *different operating system* with a minimal performance cost. If it's helped you out, consider [getting involved](https://www.winehq.org/getinvolved) or [donating](https://www.winehq.org/donate)!

View File

@ -0,0 +1,134 @@
---
title: "Primoprod Progress Report 3"
date: 2023-12-29
tags:
- tech
- primoprod
- featured
---
Welcome back to another one of these development posts! It's been almost two years since [the last one](/blog/2022/primoprod-progress-report-2), so there are plenty of changes to talk about. Primoprod is more polished than ever, but a couple of questionable initial design decisions made it harder to work on than before.
<!-- more -->
## Pretty pretty pulls
There's a summary screen for 10-pulls now! It has that nice slide-in animation from the base game too, along with (largely) proper item alignment and a sorting algorithm that is not exactly like the base game's but works well enough to generalise outside of Genshin too.
The last step for this screen is to have the rush of particles in the base game for 4- and 5-star items, but that's incredibly difficult for someone not frontend-oriented like myself.
![(SPOILERS FOR FONTAINE'S ARCHON QUEST) Hydro Dragon acquired!](primoprod-10pull.webp)
Not only that, there are lots of small improvements to better mimick the base game! For example, weapons now have their weapon background behind them:
![What a beautiful sword.](primoprod-weapon-bgs.webp)
Banner headers are now dynamically generated instead of taken from screenshots, so now they can be nicely animated!
![No more screenshots >:(](primoprod-banner-headers.webp)
Version 3.0 of the base game brought a new book UI for the details and wish screens. I'm proud to announce that Primoprod was the very first simulator to incorporate it into its design!
![](primoprod-book.webp)
## Performance and prefetching
These are only a few of the design changes that make Primoprod a prettier and happier simulator. However, beauty means nothing if no one can see it. Luckily, update 1.2.2 of Primoprod brought some much-needed optimisations that make its web version much more palatable.
As of writing, Primoprod has around 75 MB of assets that it may need to show to users, the bulk of which is wish splash art. Previously, all of this was delivered as PNGs on demand, which resulted in noticeable lag waiting for the image when going through 10-pulls. By converting media to more modern formats, Primoprod gains nearly a *5x decrease in load times!*
Specifically, all PNGs were converted to WEBPs, MP4s to WEBMs (except for Safari, which doesn't support it), and MP3s to OGGs (except for Safari, which doesn't support it).
But that's not all! A 5x decrease in load time is still noticeable load time. The user doesn't want t to wait a few hundred milliseconds when they click the wish button, so we need as little latency as possible.
As it turns out, fetching the wish animation videos while the user is chilling on the main banner screen is both incredibly easy and incredibly rewarding — as long as the user doesn't wish right away, they can download the videos in the background so that when they start gambling, they don't have to wait at all!
Primoprod uses this same tactic to prefetch pull splash screens. Because rolls are determined the instant the user presses "Wish", it can prefetch the splash images required while the meteor animation plays, which means that splash screen animations are also instant! With a sufficiently fast internet connection (~1-2 MB/s), there ends up being *zero load time* for the user.
Of course, the downloadable versions of Primoprod (desktop/Android) will still always be faster, but this is such a noticeable decrease for users that it was a no-brainer.
## Quash the larvae before they hatch
### Oh, you sweet summer child
Things have changed a lot in the web development landscape since I began this project back in July 2021, and those changes have highlighted both the bad and the good in my initial designs.
I chose to use TypeScript and Vue 3, both decisions that hold up today. However, I opted for Vue's Options API over the new Composition API, which resulted in [a headache](https://github.com/potatoeggy/primoprod/pull/54) when I tried to read my old code — not helped at all by the fact that Primoprod was my first foray into web development, and there is some truly atrocious code:
```vue
<template>
<img
v-for="weapon in ['Bow', 'Catalyst', 'Claymore', 'Polearm', 'Sword']"
:key="weapon"
:src="getWeaponBgImage(weapon)"
:class="[
{
'active-bg-img': true,
nofilter: true,
'display-none': currentItem.element !== weapon || animationIndex < 0,
transparent: animationIndex <= 0,
'animate-weapon-bg': animationIndex === 1,
},
]"
/>
<img
:src="currentItemImage"
:class="{
'animate-image': animationIndex === 1,
'zoom-image': animationIndex === 0,
'active-img': true,
'active-img-weapon': currentItem.type === 'Weapon',
transparent: animationIndex < 0,
}"
:alt="currentItemImage"
@animationstart="playSfx"
@animationend="if (animationIndex < 2) nextAnimation();"
@load="nextAnimation"
/>
<!-- the reason why the double check is needed is
that the two animations for the drop shadow count
as two animations and trigger animationend twice -->
</template>
```
Another issue I didn't foresee but still ended up to be a real pain was Webpack. [Vite](https://vitejs.dev/) is the new kid on the block, built specifically for Vue, with the major advantage of being extremely fast. At Primoprod's scale — and Primoprod isn't even that big of a project — Webpack takes more than ten seconds to start the local dev server. Which is slow!
```
Done in 12.38s.
yarn build 57.47s user 9.66s system 535% cpu 12.528 total
```
### You will never take prop drilling away from me
Behind the scenes, a lot of systems have been tweaked and changed over the years as Primoprod grew in scope. [Pinia](https://pinia.vuejs.org/) is a global store for Vue (similar to Redux in React) that Primoprod now uses to hold information such as the current banner, inventory, and settings.
Previously, all of these were drilled deep into nested components, which is really not great.
What *you*, the user, gets out of this are a bunch of bug fixes and some really nice quality-of-life features:
![Why earn gems when you can just cheat?](primoprod-settings.webp)
Now you can choose to have infinite fates! And roll only Qiqi! And even go back to older banners, a long-overdue feature.
### Quests
Quests have been rather neglected since their release pre-1.0 a couple years ago, largely because I no longer use it to stay productive. Recently, they've received some love and a *lot* of patches to fix some really strange behaviour.
![](primoprod-quests-overhaul.webp)
First, there is a character limit in the title, and the box height has been slightly increased to allow multiline titles to properly overflow. Daily commissions now have icons to make it more obvious and also to copy the base game. Lastly, a bunch of bugs related to lists and quest ordering were squashed to avoid the janky horror that was pre-1.4 quests.
### For the mobile gamers in chat
Primoprod's mobile version has received the most love of all! Strangely, in WebKit-based engines (Chrome, Safari, Android WebView), using `background: fixed` with `transform: rotate(...)` causes the browser to ignore the `background-image` property. This broke mobile backgrounds on everything but Firefox Android, so that needed to be worked around.
On Android, things have tidied themselves up a bit as Primoprod comes ever closer to being able to fool the untrained eye. The notification bar used to annoy people by being ever present in the corner — now it's gone. There used to be a strange black flash right before playing videos — now that's gone. And video playback used to be very squashed on mobile — that's fixed too!
All in all, the squeaky platform got the grease.
## The future
As Primoprod approaches its two-and-a-half-year anniversary, it is slowly time to say goodbye. Updates have become more sporadic, and even I don't use it to be productive anymore. There is only so much one can polish something before it starts to blind them. This progress report may be its last.
But it was fun while it lasted!

View File

@ -0,0 +1,53 @@
---
title: "Sunsetting the Eifueo Project"
date: 2023-05-23
tags:
- eifueo
- retrospective
---
For three and a half years, the [Eifueo project](https://eifueo.eggworld.me) has dutifully carried out its task of collecting and organising notes in a way that would be quick and easy to review. Although this worked out wonderfully in high school, the method is both inefficient and insufficient for the pace of higher education.
So how can we make it better?
<!-- more -->
We can't.
At their core, engineering courses in university are about problem-solving. Instead of blindly memorising rules to be applied once to get you the answer, you blindly memorise rules to be applied *two or more times* to get you the answer.
If we have to do that, it's much easier to write plenty of practice problems instead of rewriting plenty of notes.
## It takes too loooooong
Reformatting notes is not an easy endeavour. We have to re-examine our old notes, research any ambiguities, and then write all of them up in a way that is clear, concise, and professional. On average, I'd say that it took a good hour or so per day to finish Eifueo for six courses. Imagine the number of practice problems I could finish if I allocated Eifueo's time slot to those instead!
## It's booooring
Me? A writer? Imagine.
This one isn't actually too bad, and doing practice problems instead isn't going to help much, but think of all the other fun things I could be doing instead. I already *have* the notes. If I need them, I'll just look back at them.
Anyway, by the end of Eifueo's lifespan, most of the content was regurgitated onto the site.
## It doesn't help nearly as much for exams
This is perhaps the biggest reason, and I have to say that I'm quite disappointed in the education system for this one.
High school had many courses that were "expression"-focused, and those were the courses that Eifueo excelled in. English, chemistry, and history all were less about getting the answer or precise process correct, and more about how well you can bullshit your way out of it. Even math was pretty easy to apply a shallow formula and get the right answer.
![We love nice TAs!](thanks-192-ta.webp)
Unfortunately, the physics courses are antithetical to everything Eifueo stands for. They have a rigid structure that you can't bullshit your way out of but are also flexible enough that you can't simply apply a formula. The best way to get good is simply to do more problems.
You'll have to understand that this greatly saddened me as a person who tries his hardest to do *less* problems. The cost-benefit ratio isn't worth it anymore.
## Retrospective
With a heavy heart, I must bid farewell to one of the first services I ever deployed to my server. Eifueo has tried its hardest to help me keep up, but I can only avoid doing practice for so much before it actually bites me in the butt.
o7 It has served me well.
![At this rate, I'm going to be an expert in perspective art!](assessment-art.webp)

View File

@ -0,0 +1,67 @@
---
title: "Selected Excerpts from NaNoWriMo 2021"
date: 2024-09-23
tags:
- nanowrimo
- featured
---
You're lucky he hasn't spotted you, otherwise you might've had to talk to the man in the moustache. It's not that you're intimidated by his moustache, but adults — probably Mesa's father — are just hard to talk to. Especially if they have a glorious moustache like that. You rub the skin above your upper lip longingly. Only the tiniest of hairs there.
<!-- more -->
---
You nod politely in return, standing up straight to make use of your 162 (nearly 163) centimetres of height as best as possible.
---
How educated this man must be, to have such an open mind that he would even read the truth! And what courage to hide the forbidden book in such a copy!
Your respect for his moustache increases tenfold.
---
Chopsticks? Your eyes search for but fail to find a spoon, let alone a fork or knife. The two Baccaloreans in front of you are nomming away contentedly. Savan glances up first but returns to eating, your acting skills easily passing off your ignorance of the useless and convoluted sticks as genuine admiration of the dish.
---
Of course something is wrong, you're going to make a fool of yourself by not knowing how to use two sticks to pick up food! What's the point of sticks over straight-up using your own hands? At least refined utensils like the spoon provide extra functionality that you weren't naturally born with. How is he picking up that rice with those things!
---
Both of their eyes are on you now as you reach for the chopsticks. You can't lose face now. The handbook on Baccalorean included a section on using chopsticks, but you've never had any practical experience with them. You check that you're picking up the right side — why are they also unidirectional — and clutch them with your left hand, then deftly moving them so that they become clamped between your right thumb and index finger.
Success!
---
Ah. You'd forgotten he was asking for your opinion for the salmon. As your eyes flick to the block, your mind runs through calculations in the fraction of a second it takes for them to get there. _The salmon block is too big to fit in your mouth. Your chopsticks wouldn't be able to grip a block of that size, anyway. There is no way you can orient the block or your chopsticks to change the above two facts._
It's impossible. You have only one option.
You subtly stare at Mesa's chopsticks as they squeeze around her salmon block, forcing it into two, then elegantly twist to surround the smaller chunk and raise it to her mouth. It may be a technique you will never master.
But it will be enough for the current situation. Your chopsticks descend once more to your plate, squeezing the salmon pip until it nearly bursts, several tiny pieces that you are certain you will not be able to recover falling to your plate. A small sacrifice for the greater good. The chopsticks raise once more, this time precariously balancing a mutilated piece of salmon between their tips.
And they slide perfectly into your mouth, securing your prize once and for all, thereby ending the chopstick saga forever.
---
You keep your praise minimal so as not to inflate Savan's ego and to keep him humble.
---
"A _benevolent_ dictatorship," Savan corrects you. "Our goddess has a moderating effect on society so that no one gets into really heated debates because she's the ultimate mediator. She prevents problems before they get worse. That's compared to Constu, where you guys break things halfway through and revert to normalcy."
"Nonsense. We call that 'agile' development. It's the fastest way of finding out which things stick and which don't. My teachers always said that taking risks is an important life skill to have. Your goddess never takes risks and so you guys are all stuck in your small little bubble of stagnation."
---
"Hey, kid!"
You keep walking. They must be talking about someone else. Not only are you _not_ a child, you're doing very well collecting strawberries and nothing anyone can say is going to change your mind.
"Kid standing up with the funky hair!"
You whirl around to face the perpetrator of the grave insult, sending your sharpest glare his way. _"Excuse me?"_ Your hair is immaculately styled, not _funky_.

View File

@ -0,0 +1,227 @@
---
title: "Selected Excerpts from NaNoWriMo 2022"
date: 2024-09-24
tags:
- nanowrimo
- featured
---
Ganyu frowns as she pulls out a black leather straitjacket out of one of Yanfei's boxes. "Hey, Yanfei?" She holds it out in front of her. "What's this for?"
Yanfei looks up from organising her hats on the shelf. "What do you mean? Obviously, you wear it…?" Her expression is puzzled.
There's a small pause before the reply. "Never mind…"
<!-- more -->
Xiao stifles a snort. "That's right, Ganyu. Use your imagination. What else could you possibly use it for?" The straitjacket flies toward his face. Chuckling, he catches it and tosses it back at Ganyu.
---
"Uh oh, Xiao. You might want to reconsider biting into Jolly Ranchers around Yanfei," she teases. "She'll roast you on a spit."
Yanfei laughs while pinning pictures to the board on her closet. "The gummies get stuck on your teeth\! But imagine chewing a hard — wait." Her eyes narrow as her expression suddenly stills. "You meant the soft ones, right?"
Xiao looks away and proceeds to hang up a Drake poster.
_"You meant the soft ones, right?"_
"It's like a nutcracker going off every few seconds," Ganyu supplies.
Yanfei stares at Xiao, who is decidedly not looking at her. "How have your teeth not fallen out?"
---
"Nope. University math is not real math. There are no numbers."
---
_"Pink team\!"_ The sudden shout startles them, and they turn to see a man in a pink rabbit suit holding a megaphone. Yanfei's jaw drops. She's amazed she missed him in the first place. The chatter in their team stops. "Give me your attention\! Excellent. Now," he says authoritatively, pacing back and forth, "during this orientation, I am responsible for you. You will not like me. But you will _learn_. And you will understand, freshmen, that I speak for the university. From now on, you will only speak when spoken to. _Is that clear?"_
---
The rabbit man's mouth twitches but makes no comment. "Now, we head to the opening presentation. Do not lose me, or you will be severely beaten."
---
Miko leans in, smirking. "Don't go around underestimating UTI, now," she winks. "You'll be in for quite a bit of pain if you do."
---
"With free food as motivation, who could lose?"
---
Yanfei blanches. "Uh…" She scooches back. "You do make…food."
"Does she?" Xingqiu mutters. Hu Tao chops him again. "Ow\!"
---
"Where. Are. The. Vegetables?" He shudders. "I only saw _boiled carrots_."
They all look down at their plates. "There's napa in my _gyoza_," Shinobu says.
"Tofu has beans." Yanfei offers. "Those are vegetables, right?"
"What's a vegetable?" Hu Tao asks innocently.
---
Yanfei empties her own bowl. It soon fills up again to accommodate the recent group of refugees from her body. "It's not the tofu," she insists weakly, squeezing her eyes shut. "It's _never_ the tofu." Tofu wouldn't cause her physical pain to stand up. Tofu would embrace her and tell her everything's okay, that her stomach isn't the one cramping out and vomiting in the wrong direction, that the mouth on her other end isn't retching gravy and spitting all over the bowl of chocolate soup.
---
It doesn't stop. A river is flowing through her body. She imagines all of the microbes going whitewater rafting to exit her system. Only the water is neither white nor water. And the raft has spikes on the outside, as if designed to cause her the most suffering possible.
---
She's ascended to a higher plane, she feels. It's freeing — until her stomach brings her back to reality with a cheerful _mrrgle_ as if taunting her.
---
"Why is there so much work\! I have. Three. Tests. Next. Monday."
"Yeah," Keqing nods. "It's always easiest the first two weeks." Ganyu elbows her. "Oh? I mean — it's really rough the first two weeks."
---
Ganyu chuckles, eyes unfocused. For some reason, Yanfei feels a sense of dread looming behind her — oh, that's just Xiao.
---
"That's too much, Keqing."
"Ganyu, I need it\!" Keqing wails. "Are you really going to deny your girlfriend the only thing she truly loves?"
---
"Sorry, Yanfei. Kazuha's high again."
---
Sliding on her glasses, Ganyu reads the question aloud. "As _n_ approaches infinity, evaluate the limit of the *n*th root of the sine of pi over two _n_ times the sine of two pi over two _n_, all the way up to _n_ minus one pi over two _n_…" she murmurs. "Okay. What don't you get?"
---
"No, you messed up again here, I'm not sure how you got that. Five plus two isn't seven, it's… Oh. Wait, it is seven."
---
Xiao continues to watch television. "Yes. Yanfei helped. Your recipe is quite based — there's more left in the fridge for you."
Ganyu pauses. "Huh. Thanks. Nothing burned down, I hope?" She peeks around as if searching for scorch marks.
"No. Yanfei almost tried to grind the almonds with an egg beater though. That was cringe and unbased."
A few seconds pass in silence. Xiao looks up. "Ganyu?"
Ganyu stares at him with an expression of unbridled horror. "W-what happened to you?"
"Is there something that is sus? Yanfei taught me some common slang used by university students. That was very poggers of her."
A quiet _thump_ resounds as Ganyu sits down heavily on the nearest chair she can drag over. Eyes unfocused, she gazes into empty space. "No — just —" She abruptly gets up. "I'm going to talk to Yanfei."
"Kek-double-U," Xiao states.
---
"Good decision," Yanfei nodded. "If you don't overflow on CRIT Rate, it's your best-in-slot in freeze teams. Second BiS in melt, too."
---
Evidently, the fish did not want to be a new member of Yanfei's family and splashed out of the chest right onto her face.
---
Yanfei nodded agreeably. "Yeah, Magikarp kinda sucks. The only thing it does is splash around. Magikarp," she declared, "you have disappointed me for the last time. By the power invested in me by myself, I sentence you…to exile\!"
---
She shudders, imagining the explosion in her gut if she had to live off of dorm food. Then the state of the communal bathrooms for everyone who has to live off of dorm food.
Nope. She's _very_ glad to be living off-campus, actually.
---
She can only imagine the level of flex Hu Tao has by having the ability to hand out _bubble tea_ to people who come over.
---
Yanfei's fake moustache falls off from how long her jaw has been stuck to the table. She pushes up her sunglasses, rubbing her eyes as if she can't believe what she's seeing.
---
"I mean, I totally get why," Hu Tao says dreamily. "Yun Jin's a real piece of eye candy. I just want to _bite_ into her pompoms\! It's too bad she doesn't lean that way."
---
"Here in my apartment, we've just got this new blender, so don't mind Ganyu being vegetarian over there. But you know what I like a lot more than materialistic things?"
Yanfei points at the television. "Knowledge. And where else can you find more knowledge than in appreciating art?" She claps her hands. "So that's why we're gonna play Mario Kart\! I read in a book somewhere that competition nurtures the mind."
Hu Tao nods along like it all makes perfect sense. "So if I win, I get smarter?"
"Damn," Shinobu says. "We're all gonna be geniuses by the end of this. Except for Hu Tao."
---
He knows her much better — she wouldn't get him something mortifying like a _Link body pillow_. Ganyu's box is smaller than Yanfei's, which is a strong sign that it doesn't contain a Link body pillow.
---
"I noticed that your Pikachu pyjamas were starting to wear out. You must have had them for _years_ now. So I got you another pair\!"
---
"By the way," Yanfei says as Xiao takes his very first bite, "we knew you didn't want something too unhealthy for breakfast, so we tried out spinach as the main ingredient this time. What do you think?"
---
A girl and a boy sit in a Meet Fresh booth, its logo prominently displayed on the wall behind them. "Have a shaved ice," the girl whispers to the boy in the movie. "It's delicious. Made out of 100% fresh fruit. Just like me."
---
Yanfei averts her eyes until the subtitles stop saying \*\*SLURP SLURP SLURP\*\*.
---
"Then don't you want to eat me out?" the girl says huskily, caressing his cheek. "Like you eat out Meet Fresh's watermelon shaved ice, only available here for $9.99?"
"But isn't that expensive?" The girl gives him a look of utmost concern like he's just said that he's about to die.
"Our love is worth it," he reassures her. "Just like how the taro red bean soup is worth the $5.99 at Meet Fresh." He pulls out the dish from behind him. It looks overly bright and shiny, almost exactly like the picture in the menu. "Wouldn't you like to try one?" The boy takes a sip, then holds it up to the girl, who lovingly meets his gaze as she eats the rest of the spoonful. Both of them sigh with joy in unison.
---
Instead of the usual beach boy masquerade, today Xiao's hairdo looks like Morax decided to play cat's cradle with it but got bored before he finished.
---
"Welcome to the fourth meeting of the Debate Club. May we recognise that we are nothing but pawns to the great…Debate Club." She holds out a spiked metal bat to the skies.
---
"Objection\!" Yanfei buzzes. "Chewbacca override."
"Sustained. Sumeru, please be aware that the club formally forbade use of the Chewbacca Defense effective the 21st of November."
---
Yanfei spreads her arms. "Respectfully, Fontaine requests that the opposing counsel consider the implications of curves on pancakes. In reality, the inherent lack of structure _inside_ the pancake must lead to highly undesirable flopping, just like Mondstadt's tiny pp when he sees one\!"
Chongyun slaps the table, standing up and pointing at Yanfei. "Objection\! Inadmissible evidence\! The counsel from Fontaine…has highly exaggerated the size…of my colleague's…" Xingqiu practically drags him back down beside him, mildly red.
---
"It's okay. It's not actually losing if you lose against Numeron."
---
"They are. For this question, you're supposed to apply IBP twice. See how you can rearrange it with _u_\-substitution to make negative _x_ squared with _e_ to the negative _x_? That leaves you with another integral of negative _e_ to the negative _x_ by two _x · dx_. Then by doing IBP again, you end up with this negative _e_ and a quadratic."
---
"Is this really life, though? Dogs chasing their tails in circles over and over again?"
Yanfei nods firmly. "It is the very pinnacle of life. The epitome of all we strive for."

View File

@ -0,0 +1,367 @@
---
title: "Selected Excerpts from NaNoWriMo 2023"
date: 2024-09-25
tags:
- nanowrimo
- featured
---
My, little Peony must have such pain in his heart to so coldly shrug off an earnest request. Perhaps he suffered from childhood trauma in which his family was brutally murdered and thus he was forced to be independent, grew up much too fast, and now his heart is locked away, waiting for someone with a kind heart to bring him back to the world. But I digress.
<!-- more -->
---
A rather melancholic tune, if I do say so myself. If I were forced to speculate, I might say that he suffered through a traumatic in his adolescent years that closed him from the outside world. A troubled soul, lost and alone, dreaming of the golden years of his past\! But I digress.
---
"Sialia, humans are fascinating characters. Oftentimes quite dim, but fascinating nonetheless. Simply because _you_ have the brain the size of an earthworm — don't chortle on me now — does not mean that my _dear, cherished_ grandson isn't intelligent enough to desire a broader perspective."
---
Don't give me that, young sir\! If your wings are sore already, you'll be stuck in Los Angeles for the winter\! And what sort of self-respecting bluebird would let themselves stay in _LA_? The place is for lazy, sheltered, never-had-to-work-a-single-day-in-their-life cocks, that's what it's for. I shan't let you do it, not while these old bones can still slap you out of the sky\!
---
Are you still sore? Yes? Stick a worm in it. Here. Some nutrition. _Properly_ prepared earthworm, not like that bland, processed feed I came back to last night. I had thought your mother would have properly taught you how to feed yourself.
---
Keep an eye on the blue car beast. Humans feed them stinky earth juice, and in return, they regurgitate the humans when the human feels like it's traveled far enough.
---
Observe. This is one of the many human education institutions. Humans are incredibly stupid, so unlike us bluebirds, in order to do anything, they have to attend years of classes simply to learn what they can and cannot eat. See? They can't even eat nuts. Truly, I pity the poor species.
---
If there is one appendage to admire from the humans, I must say it has to be their nose. Not only can they smell far better than we can, it's so expressive when they wrinkle it so.
---
What a wonderful girl. When she wants something, she takes it. Be like her when you grow up. She also understands what power she has, and makes full use of it. If you never show off your skills and abilities, you'll never attract a good mate. It's important that every single bird around you knows just how competent, how amazing, how fearsome you are. _Oh, David…_
---
What's a little bloodshed between friends?
---
My, the poor girl must be suffering from whiplash more than the time I slammed into a car's windshield\!
---
"No, I can't\! Every time I even _think_ about being nice to that girl, I want to sock her so hard that her head springs back to knock both of us out so we don't have to interact. Please, Brooke. Be my Pamela shield."
---
They call it The Spanking. When a student and a teacher hate each other very much,
---
Lady, if your eyebrow lifts any higher, it might grow wings and migrate to Vancouver with us.
---
When you grow up, dearest Archie, desire nothing but to survive, thrive, and bear children. You'll be happier that way.
---
What is this nonsense? If you can back up anything you say by beating other birds up, you're right. Who's going to tell you otherwise? This isn't the first time I've heard this argument from humans. No wonder they bicker so uselessly among themselves so often and never get anything done.
---
Mind your wings — best not to get lost in the scent of Subway, lest you become addicted. Your great-grandfather was once a renowned human-watcher, but he strayed too close, too many times, and was Subway-ridden for the rest of his life. Couldn't fly a quarter mile away before his wings would lock up and he fell out of the sky.
---
Hm. Perhaps there is a flaw in this human. I do not believe that I have ever seen so many crosses.
---
We do not poke fun at those unable to fly — we can only look down upon them, sympathise, and offer our condolences.
---
Get up, or I'll make you live in Los Angeles for a week\!
---
I was once an engineer, you know. I was present at the founding gathering of the International Engineering Society. _Delicious_ seeds. Truly some of the best food I've ever had.
---
So what if you've heard it three times? This is an important cultural milestone of our species' history\! You should listen to it at least ten times\!
---
Perhaps I chose a flawed human. I do not believe that they are supposed to sprint across the middle of the road quite like that. Or be struck by their car companions like that.
---
Personally, I must interject to say that carrots are an abomination. Along with potatoes. No fruit, and you have to pull them out of the ground to eat them. And what do you get for all that effort? A bloody chore of a food, that's what you get\! Disgusting, starchy, barely juicy things, those are. Humans are a truly a different breed, they are. I'll forgive the child just this once for this transgression.
Bah. Mind your tongue. Simply because you hold objectively incorrect opinions close to your heart does not mean that you can spout off such nonsense in front of civilised birds such as myself. Didn't your father tell you to respect your elders? Be grateful that I refrain from using strong language in front of underage birds.
---
One of the any churches in the world. Legend has it that some idiot human strapped himself to the cross and got killed.
---
No, Elizabeth, snap yourself out of it\! Remember the vomiting. Remember the vomiting. Yes. Ahh…
---
What kind of stupid, arrogant, _deranged_ bird would voluntarily come to _Los Angeles_?
Oh, my. Ahem. What I mean to ask was — what kind of strong, dashing, chiselled, courageous, alluring, scarred-backstory bird would voluntarily come to _Los Angeles?_
---
"Liz, I swear, the next chance I get, I am going to take the gleeful, probably cancerous prick growing out of your head and ram it up your —"
---
"I still cannot believe that you're here because you wanted to check out a hot bird."
I very explicitly _did not_ say that, Sialia. Stop putting words into my beak.
"Right. How did you put it? 'The most incredible bird you've seen in years. His physique, his manner, his scars'? I can't possibly imagine how that could be construed as something even remotely romantic. No, not at all."
Your harsh words wound me. Still so sarcastic.
"She calls me harsh\! Archie, who do you think is harsher? Me or your grandmother? It's okay if you say me. Liz is completely harmless."
What did you say? Are you calling me _old?_
"Aren't I?"
Why, Sialia, if I weren't so generous, I would have your head pinned to the ground under my claw until you begged for mercy.
"Aw, thanks\! I have to say — you have better lines this time. If you want to get this William Swainson —"
That's William _John_ Swainson to you.
"— this William _John_ Swainson — between your legs, you have to try a little harder than _that_."
I have _years_ of experience, Sialia. I'm not a fledgling anymore.
---
Don't be so picky, child. Eat it. You heard Sialia. She'll be very much heartbroken if you don't try it, you know? She'll fall over, bawling her eyes out to the world, body wracked with sobs at the sheer offense brought to her by one small bluebird named Archie. But I digress.
---
Ah, young love. The boy and the girl look so happy together. You know, child, that in my youth, I was _hounded_ by men for who they thought I was? It is wonderful to see that today's human youth have moved on past such frivolities. To my knowledge, it is still an ongoing issue with us. If the boy and the girl were bluebirds, they would treat every interaction like a transaction, until they realise that it isn't worth being so on guard to every single person around them and decide to love each other unconditionally.
---
I have seen humans gleefully _stab_ each other simply because of infatuation, just like bluebirds. Literally _every single_ couple in human society has major issues that prevent them from being a perfectly functional unit. Every single one.
---
Ice cream is another food that will kill you. No matter how much sugar you detect, it's poison\! All poison. Humans should be ashamed, leaving out bait for good, honest birds to consume. And it's not like they eat them either\! We simply die for nothing. How incredibly rude.
---
"It exists. Therefore it's wrong. And because you're defending it, it must be wrong. See how everything logically ties in together so neatly?"
"I'm gonna throw you off the hill."
"With your noodle arms? Fat chance."
"These noodle arms picked up your skinny ass once before, Jeremy."
What did I tell you, Sialia? They're already threatening violence. We just skip the discussion bit and go straight to the violencing. Another way how birds are significantly more efficient than humans.
---
Every human relationship ends up like this eventually. They argue back and forth with each other until one of them breaks.
"Archie, don't listen to your grandmother. She doesn't understand a bit of friendly ribbing. Banter, if you will."
_Banter?_ You call this _banter?_ No wonder you don't have a partner, Sialia. If you consider this "friendly ribbing", I shudder to imagine what you must consider verbal abuse.
"Have you listened to yourself recently?"
---
"Just one question. Did it hurt?"
"What do you mean, sir?"
"When you fell…"
"I didn't fall?" An important lesson to you, child. Although the girl has a blissfully unaware expression on her face, you must hone your instinct to realise that we are moments before disaster. Sialia, if you don't let me leave, I will hide ants in your nest.
"…from heaven."
I'm going to vomit. Let me vomit, Sialia. You can't tell me what to do. I don't care how much seed will pour out of my beak. I cannot spend one more minute listening to this sappy nonsense.
"Uh, it's a metaphor. It means you're beautiful. Are you an angel?"
"…No. Thank you. Will that be all?"
---
"Yeah, you'd like that, wouldn't you? You dirty, dirty freak."
"Yes, I'm your dirty, _dirty_ freak\! I want it so, so much."
"Now get on your knees. The handcuffs stay on. That's an _order_."
"Ha… Yes, yes, of course\!"
"Did I say you could talk back to me?"
See? Now _this_ is proper flirting. Mind the whip. At this range, it could clip your wing off. If I were a human, I'd marry this human right now and here\!
What are you two looking at me like that for?
---
"Sorry about that. You must be tired."
"Yeah."
"After running around my head all day."
No, _do not smile, girl,_ what is wrong with you. I have had it with this couple\!
"That was terrible and you should feel terrible."
"Aw… But you love me anyway. You smiled\!"
"I did and I hate it. But I love you anyway."
---
"Liz, sometimes I get the feeling that you're not listening to me."
Of course I do. When have I ever not acknowledged anything you've said?
"I mean, I feel like you never really consider it."
I consider everything from everyone at great length. I am always correct, after all. In order to be so correct, I must acquire any new information as fast as possible.
---
If I hear that _inane_ word one more time, I might have to commit bodily assault on one of the humans here. Thank you, child, I do not need the earplugs.
"Dude\! You're so based. It's so fire, bro\!"
---
"Rio, pass me the laptop\! To save the dragons, I've got to hack into the Nomekop mainframe and eliminate all traces of Aderyn before we land\!"
---
I can only imagine the people at the ends of those sticks, poking and jabbing him in all the most sensitive places to make him squeal.
What do you mean, _you'd love that?_
---
"Maybe I should get a new cardigan for myself. Just in time for spring. How about this purple-and-yellow polka dotted one?"
Gah\! My eyes\! The contrast is too great\! Yet I can't tear my eyes away. It is — it is _objectively_ offensive to the eyes.
"Gah\! My eyes\! That is the most _offensive_ cardigan I've ever seen. I didn't even know they could make them like that. You should get it."
"You think so? I think it'd be funny to stroll around in this. Change up the look a little."
"I agree\! You should totally buy it."
"Hang on, let me get someone else's opinion on it. Hold my stuff while I change?"
I see. She must remove her existing outer layer of clothes before putting on a new outer layer of clothes. Such a hassle that they even have a dedicated private room for it.
Oh my goodness. I cannot look at her. I think I might go blind. My eyes feel like they're flying into a glass sliding door over and over again. It's horrifying.
"Honestly? It's better than I thought. For some reason, it doesn't look nearly as offensive on you as it did alone. They won't run you out of the store for this or anything. I'm still going to pretend that I don't know you." Pardon me? Madam? Do you need to see an eye doctor? Perhaps acquire some glasses? Are we looking at the same shirt? Did your eyes perhaps lock themselves into place looking at her face and steadfastly ignoring every other part of the outfit for the sake of their sanity?
"Strange. Don't be a scaredy-cat, Shayla. I thought it was abominable. Come on. Excuse me? Hi." She's roped in a new poor woman. May Garuda have mercy on your eyes. "So sorry to bother you, but on a scale from one to ten, how offensive is this outfit?"
"One. No contest."
"One being least offensive."
"Yeah, still a one. I feel like I can barely see it. It's pretty hard to notice." Her eyes are locked onto Mira's face. No wonder it's difficult to notice. The sight is elicits such a visceral reaction that their eyes dedicate all of their power toward self-preservation.
Mira must not be able to see the full thing, so when she looks down at her cardigan and back up, she doesn't get the full effect.
"Huh. Thank you very much. Shayla, I think I want a third opinion. I think maybe both of you are gaslighting me or something."
"It's fine, Mira. How could we both be gaslighting you?"
"I don't think I saw either of you actually look at the cardigan. It kinda felt like you were staring directly in my eyes the whole time."
"No way."
"Let me find someone else who might have functioning eyes. Ah. Excuse me\!"
"Hello, yes? How may I help you — _good glory\!_ My _eyes,_ my _EYES\!_ Ma'am, I am so sorry, but my brain is restraining me from looking in your direction — it's the cardigan, I swear, not you personally."
I knew it\! Finally, my opinion validated. I was beginning to wonder if our superior inhuman eyesight was the only thing that let us recognise truly how ugly that cardigan was.
"Thank god\! I was about to believe my friend when she said that it looked fine. How could dull yellow polka dots work on a purple fabric?"
"Absolutely — wait. Was that from _here_?"
"You bet. Picked it up from the bunch of cardigans over there."
"Yeah, no. That is not okay, ma'am. Sorry, but — ahem. In my professional opinion, I would advise you remove that cardigan and let me take it to the incinerator."
Such a wise employee.
"I kinda wanna take it home, though. It gets really good reactions."
"Look, ma'am, if you want to take it home, I'm not going to stop you. But I hope you know what you're doing."
"Of _course_ I do. Right, Shayla? When have I ever not known what I was doing?"
"Shockingly, very few times."
"Exactly. So let's go check out. I still want to visit the market on Main Street going on this week\!"
---
It's a classic\! A man and a woman meet in a bar. She comes here looking for a drink and an escape from her reality, but instead, she finds a man who, although he seems aloof, is actually the mask in front of a troubled soul who, after a great shift in his life, is trying to get his life back together.
---
All of your problems are solved now that you've found a man\!
---
I have seen _worms_ inching faster than these two progressing their relationship.
---
Look at the development. The progression. At this rate, they might even get together before I die.
---
That sputter is reminiscent of the tractor that killed your Uncle Jordan.
---
Now, some mouth-to-mouth action — that's what we're here for. The way the human lips lock together…the way the heads move as one as hands scrabble for grip… Even an old bird such as I can tell how much passion there is in a good, long kiss\! If birds had lips, Marty would be blown away by how much love I'd make to him.
---
"Let me show you how to use a carving knife…"
I wish he would show her how to carve out a future for them together instead.
---
"Son, maybe the times are changing. And I respect that. But no matter what happens, never lose that fighting spirit. Can't fight the girls? Fine. There's still a good half of the population you can beat some sense into. Now, if they say that _fighting is bad_ and that _no one should do it_ or whatever? That's when you put your fist down and screw the rules. Let no one tell you what you can or can't do."
"I'll do my best, dad\!"
"I know you will, son. Make me proud. Nothing like a good fist to the noggin to help loosen any lips."
Wonderful parenting. _This_ I can get behind one hundred percent. I know I have a lot of negative things to say about humans, but in reality, they're so diverse that there are so many different viewpoints to learn from. There are some that are objectively wrong, some that are objectively right, and then most are in the middle. It's rather refreshing to have one's opinions validated every once in a while.

View File

@ -2,10 +2,12 @@
title: Run On title: Run On
date: 2020-04-16 date: 2020-04-16
tags: tags:
- complete nonsense - complete nonsense
- you could say that the story *ran on* badumtiss - you could say that the story *ran on* badumtiss
- these are like ao3 tags now - these are like ao3 tags now
nopreview: true nopreview: true
--- ---
<!-- more -->
I was running from a dark thing and it was all horrible when my eyes snapped open and I looked at my alarm clock, which said that it was ten in the morning, which meant that I was going to be late for school unless I teleported back in time, so I got up and rushed to put on my clothes and do everything, then I didnt even eat breakfast as I rushed to school before realising I could use my magical powers that I could as soon as I turned eleven to turn back time and avoid all this mess, so thats what I did, but only after I actually ate breakfast and took my time, then I looked deep into myself and used my powers from friendship and determination to go back three hours (my friends were the bestest ever after I saved their lives using my magical powers), and I walked calmly to school, trying not to blush at my boyfriend (who is actually the hottest werewolf ever) when he suddenly popped out of nowhere, and I clutched his hand because I loved him so much, then he said, “oh hi babe whats up?” then I said, “oh nothing,” then I giggled because he was so funny and then he laughed because I laughed then I laughed because he was laughing because I was giggling and everything was awesome, until suddenly a tree burst from the ground and we couldnt get to school because suddenly there were tree monsters everywhere and I got scared but I knew that John (the werewolf) would protect me because he was a werewolf and so he transformed into a blue wolf (the rarest and most powerful kind) and after that he used his magical powers to vaporise the tree monsters with flashes of light and they all disappeared, and then I hugged him because I was scared and I said, “I was scared, thank you,” to which he said, “oh its nothing I would always protect you, my love,” and then I giggled again because he was being so cute, so I stared into his brilliant blue eyes which I only just realised matched the colour of his wolf form and admired all his really big muscles which would save me from any tree monster, but then suddenly he tensed and I realised that he was tense so I ran behind him again and peeked out from behind his shoulder and saw that something terrible appeared (even worse than tree monsters!), it was a ghost, and that was the first time I saw my boyfriend scared, which I could feel using my psychic powers so I told him telepathically that it was going to be ok and I could handle it using my dark powers which were super effective against psychics, and I focused really hard but nothing happened, so I thought of the strong bond that I had with all my friends and begged them for their help because there was this ghost and it could destroy the world, then they sent all their power and my eyes glowed as I tapped into a part of my power that i had never seen before, but it was now unlocked because of my friends determination and courage that flowed through my veins, as a white light burst from my hands that I held outstretched in front of me when I realised that this was the dark thing I was running from in my dream this morning, which I would finally kill because of the power of my friends that I had to see at school today, and I shouted as I killed the ghost, “omae wa mo shindeiru,” and it exploded and so we celebrated with my friends and they were all super impressed as I told the story and smiled at my hot boyfriend. I was running from a dark thing and it was all horrible when my eyes snapped open and I looked at my alarm clock, which said that it was ten in the morning, which meant that I was going to be late for school unless I teleported back in time, so I got up and rushed to put on my clothes and do everything, then I didnt even eat breakfast as I rushed to school before realising I could use my magical powers that I could as soon as I turned eleven to turn back time and avoid all this mess, so thats what I did, but only after I actually ate breakfast and took my time, then I looked deep into myself and used my powers from friendship and determination to go back three hours (my friends were the bestest ever after I saved their lives using my magical powers), and I walked calmly to school, trying not to blush at my boyfriend (who is actually the hottest werewolf ever) when he suddenly popped out of nowhere, and I clutched his hand because I loved him so much, then he said, “oh hi babe whats up?” then I said, “oh nothing,” then I giggled because he was so funny and then he laughed because I laughed then I laughed because he was laughing because I was giggling and everything was awesome, until suddenly a tree burst from the ground and we couldnt get to school because suddenly there were tree monsters everywhere and I got scared but I knew that John (the werewolf) would protect me because he was a werewolf and so he transformed into a blue wolf (the rarest and most powerful kind) and after that he used his magical powers to vaporise the tree monsters with flashes of light and they all disappeared, and then I hugged him because I was scared and I said, “I was scared, thank you,” to which he said, “oh its nothing I would always protect you, my love,” and then I giggled again because he was being so cute, so I stared into his brilliant blue eyes which I only just realised matched the colour of his wolf form and admired all his really big muscles which would save me from any tree monster, but then suddenly he tensed and I realised that he was tense so I ran behind him again and peeked out from behind his shoulder and saw that something terrible appeared (even worse than tree monsters!), it was a ghost, and that was the first time I saw my boyfriend scared, which I could feel using my psychic powers so I told him telepathically that it was going to be ok and I could handle it using my dark powers which were super effective against psychics, and I focused really hard but nothing happened, so I thought of the strong bond that I had with all my friends and begged them for their help because there was this ghost and it could destroy the world, then they sent all their power and my eyes glowed as I tapped into a part of my power that i had never seen before, but it was now unlocked because of my friends determination and courage that flowed through my veins, as a white light burst from my hands that I held outstretched in front of me when I realised that this was the dark thing I was running from in my dream this morning, which I would finally kill because of the power of my friends that I had to see at school today, and I shouted as I killed the ghost, “omae wa mo shindeiru,” and it exploded and so we celebrated with my friends and they were all super impressed as I told the story and smiled at my hot boyfriend.

View File

@ -0,0 +1,80 @@
---
title: "ECE 192 Words per Minute"
date: 2024-02-29
tags:
- featured
- shorts
- university
---
**Summary:** A student is mildly frustrated by his professor's pace of speaking.
<!-- more -->
---
"Even if we use one million, or two million, or even three million pieces, the additional tooling cost will remain constant. So you can see that in order to make a good economic analysis, you have to classify the costs related to the output activities as follows…"
Lento slammed his pencil down as the professor *finally* stopped speaking. The soot streaks on his notebook smoked from his rapid scribbling, and his wrist shook from the sheer exertion of trying to take notes from a professor who read off slides that looked like someone tried to squeeze a novel onto them.
Judging by the fire alarm going off around them, he wasn't the only one who nearly combusted his notes.
"You know," Atras murmured from beside him, "online lectures sound like a really good idea right now." His notebook was closed, his pencil pristine. The signs of a man who gave up long ago. The signs of a smart man.
"I," Lento hissed, "cannot take this anymore."
"What is he even saying?" Atras said, calmer but still exasperated. "What's all this about costs and benefits and graphs?"
"Who *cares?* Why does this *matter?* Has this man *ever had to deliver a presentation before?* I *don't need this to pass my exam.*"
Atras patted his back, an understanding but exasperated expression on his face. "Calm down. It's not that bad. It's just a humanities course."
"I'm not seeing the humanity," Lento said grimly.
"It's the bird course. The easy course. The course you don't even have to take notes in. Why are you trying so hard, anyway?"
"Says the guy who isn't taking notes!"
Atras snorted. "The prof posted all of his slides online. Why *should* you take notes? I'd rather not develop carpal tunnel in the first week."
A clock *tick*ed and Lento glanced over to the clock. The professor starts talking again, the sound like a drill burrowing directly into Lento's ears as the added knowledge of *incompetence* makes the emotional damage all too clear.
"How is he still talking?" Lento's voice steadily rose in pitch. "What is there left to talk about? Why can't I go home? Why am I here? Just to *suffer??"*
"And that's it!" the professor said brightly. "You are now free to go. Tomorrow, we'll be looking at cost estimation models. There are practice problems on LEARN if you want to apply what we covered today."
A rush of cool wind blew from somewhere out the exit, even though that would make no logical sense whatsoever, but conveniently masked the students' collective sigh of relief.
Lento stood up, dumped his notebook and pencil straight into his backpack, and promptly dragged Atras out the lecture hall with him. "Out," he demanded. "We are out of here."
"For the last time?"
"Do you even need to ask?"
"I thought it was interesting," said Atras. "The professor sounded like he knew what he was talking about. He just covered things a bit fast, that's all. And kinda derailed sometimes."
"…And he went *on and on* ***and on and on*** <u>***and on and on*** <span class="text-xl">***and on and on***<span></u> to say the equivalent of a small glossary and four lines of notes, except you don't know what four lines you need so you write down all of them anyway."
"You're too bitter," Atras observed. He held out a red rectangle. "Want a KitKat?"
"…Yes." Lento snatched the candy out of his friend's hands and ripped the wrapper open. He chewed methodically on the chocolate.
"Feeling better?"
"A little," Lento admitted. "But still. I'm not going back to that class. "
"You should," Atras coaxed, holding out a Coffee Crisp. "You always learn things better when lectures can hold you accountable. Here, do you think you can talk about the course without hating on the professor for one second?"
"Who's gonna hold the professor accountable?" Lento grumbled. Atras gave him a look. "Yeah, yeah, I know. Fine. One more class. He has *one more chance."*
---
Addendum:
"If you have three horses, the magnetic flux through each horse actually approaches zero as we move farther and farther away. Combined with Gauss' law, given that we know that the flux density of each horse is, in an ideal world, equal to that of free space, this lets us cancel out the electric flux density factor on both sides here, which results in seven horses!"
The hissing sprinklers and wailing fire alarm do little to stop the madman from his lecture. Lento rests his head on his arm on his desk, his pencil having been vaporised long ago. "Surely this isn't on the exam. What kind of econ is this?"
"Be sure to remember this, because it'll be a major part of your midterm!"
Atras pats his head consolingly.

View File

@ -0,0 +1,294 @@
---
title: Glorious Pain
date: "2024-11-22"
tags:
- birdseye
- nanowrimo
- featured
---
**Summary:** Liz, Archie, Sialia, and William J. Swainson watch a human who insists that what doesn't kill you makes you stronger. And, well, who wouldn't want to be stronger?
<!-- more -->
Good morning to you, child, Sialia, Mr. Swainson. Shall we start the day?
"Why, but of course. Are you all prepared for human-watching? Have you everything you need?"
Of course. Let us travel across the skies of Winnipeg!
"Excellent. Please follow me. The Forks are rather busy this time of year. If I ever needed a quick human, the Forks were a fine place to find one."
What accessible architecture! The highs and tumbles all mesh together beautifully. A bird could hide or swoop around this area anywhere!
"Another reason why it is such a perfect location for birds. Look at those humans arguing over there. That's usually a good sign to find interesting people."
Let us listen in, then. What an angry woman, pulling her friend close to her like that. That other man might be getting a little too close to them for comfort.
"Get your hands off of him! How *dare* you insult him like that? Do you know what he's been through?"
"Woah, dude. Take a chill pill. I didn't even touch your friend there. I just called him out for being rich. Y'know, got his whole life sorted out before the age of ten by his parents and all that. You don't gotta act like I slept with your mom last night."
"Um, Sera, it's okay. Isn't this a bit of an overreaction…?"
"No! Absolutely not, Leo. If anything, I can't believe that you aren't reacting more! How can you let him say that to you? You lived in poverty since you were three. You scrimped and saved and worked as hard as you could just so you could feed both yourself and your family while going to school. *You deserve nice things.* It's okay to want for things."
"Damn, man. I didn't know you had a backstory like that. Sorry, bro."
"Yeah, well, you should apologise! Don't judge people by how they look, loser."
"Come on, Sera. I'm sure he didn't mean any harm. People are staring. Let's just go."
"Hmph. You value yourself *way* too little. This world is full of injustices. I just want to see you succeed, Leo."
I daresay that this might be the most interesting human yet, this *Sera*. A blunt and direct person, isn't she? Let us follow these two as they leave the malls.
"Seraphina!" A new girl.
"Oh, hey, Amara! You can totally call me Sera, you know that, right? What're you doing here? Oh, this is Leo, by the way. Leo, this is Amara. We've been friends since high school."
"Heya. Great to meet you!"
"Nice to meet you too, Leo. Seraphi — sorry, I mean, *Sera*, I was gonna go shopping with Phoebe today. Wanna join us?"
Out of all of the noses I've seen, Sera's nose is up there for most expressive. That wrinkling is remarkably clear, even to birds such as myself.
"Phoebe? Phoebe Sinclair?"
"…Yeah. Is something wrong with Phoebe?"
"Not at all." That sniff says otherwise. "She's perfectly fine the way she is. I just wish I understood how she got to where she is today."
"Huh. Okay. Well, maybe you'll get to talk to her at Ethan's birthday party today. You're going, right?" Birthday? Oh, we must follow this girl.
"Of course! Wouldn't miss it for anything."
"Cool, cool. Phoebe's been waiting for me at Zara for a while now, so I'm gonna dip. See you tonight?"
"Yeah, for sure. Bye!"
"Nice meeting ya."
Child, this is a once-in-a-year opportunity! Humans' birthdays are incredibly significant to them, and the particularly interesting ones make a big fuss out of it and throw a party. You're incredibly lucky to see something so special at your age.
Mr. Swainson, please don't raise his ego. Garuda knows it's flying too close to the sun already. The humble bird gets the worm, as they say.
---
One more piece of advice to you, child: You can easily tell if a house contains a lot of humans by looking at the number of cars in the driveway. Usually, those with more guests have more cars, as the cars refuse to leave without their owners. From there, you can use the information in a variety of different ways. You could track the humans entering and leaving the home based on what cars they take and how many humans each car holds. You could also link the cars to the home and build a network of humans. The possibilities are endless!
In this case, we can use the information to easily verify that this is indeed the correct house. The richer humans with the larger houses are particularly nice because they usually have at least one open window somewhere that is suitable for human-watching. Additionally, the density of humans in the house is lower, so your chances of getting caught are much lower.
For now, let us find a nice open window to eavesdrop from. Oh! Actually, we can approach the window later. A new car is pulling up to the house!
"Hey Ethan — happy birthday! And Sera! When was the last time I saw you guys?"
"Phoebe! C'mere. Oh my god, it's been way too long. Love the necklace, by the way."
"Thanks! My mom helped pick it out for me last week."
"Hmph."
"Hm? Sera?"
"Nothing. Don't mind me. Why don't you come in?"
It's going to be difficult to squeeze by them. Shall we head to the window to continue their conversation?
"…and then I told my mom, so she was like, 'Do you need a new one?' And of *course* I'm not gonna say no to a new phone, right? Sera, is something wrong? You've been kinda smug this whole time. I know that we've had our disagreements sometimes, but *surely* you're over it now?"
"Oh, but of course. How could our *little princess* here be troubled? *Everything* must end up perfectly for her. Oh, the horror? How could you possibly be bothered by something as trivial as *disagreements?* 'No,' she says, 'look at me! I'm so perfect and smart and pretty and I have so many friends!' Even though you haven't done a single *damn* thing to deserve it. Acting so high and mighty like that — it disgusts me."
"What are you on about? Did I do something to you?"
" *'Did I do something to you,'* she asks. More like what you *didn't* do. *You're* probably just here because you're Amara's friend. Honestly, I don't know what she sees in you. Have you ever been grateful for anything or worked a *single* day of hard work in your entire life?"
"Hey! Don't mock me. Look, I don't know what your problem is, but this is getting out of hand."
*"My* problem is that *you* don't realise just how good you've got it while you brag about all of the *nice things* you have to other people. Actually *good* other people, unlike you. See Amara here? She *chooses* to be kind even though she's experienced first-hand how cruel the world is. She lost the bright, innocent spark in the eyes of her childhood when her parents died, when *no one* wanted to help her, and when her closest friend betrayed her. Yet *still —* still she has ten times the heart that you do."
"Sera, *please* calm down…"
What a proud human. But strangely…not for herself. She's proud of the people around her. Quite refreshing. I'd say that I've rather had enough of the usual narcissists. And, if I may say, with rather strong, correct opinions too. Listen closely to every word she says, child. There is much you can take from her insights.
"I will *not* calm down, Amara! How can you possibly be so calm when there's someone like *her* right in front of you, spouting things off like she knows everything?"
"I'm just a normal girl, Sera. It's not that big of a deal."
"Have you seen her *house*, Amara? She practically lives in a mansion. Her whole life, she's never had to worry about food, about having a roof over her head, about if she'll be alive tomorrow. And now she *dares* enter into your lives without even a single sacrifice?"
All excellent points. Child, no matter how old you may be, you will remain a child in my eyes until you suffer true hardship. Learn that there is no free lunch in this world. Learn that if you truly want something, you need to *work* for it. If you don't work for it, you won't get it. It's as simple as that.
There is no sense believing that all of the puzzle pieces will align one day, and suddenly everything will fall into place. That's at best a fantasy that should be relegated to stories. How can you possibly be happy if you've never been truly sad? How can you recognise success if you've never failed?
…Respectfully, *William*, I must disagree. Life is nothing without suffering. If one does not suffer, they cannot understand how other people who truly *do* suffer feel. Those who experience more suffering are objectively better. Not knowing that only means that you have not experienced enough to understand the incredibly large range of emotions and experiences one can go through. I'd recommend going out and travelling more.
"Woah, woah! What's going on here? Ladies, ladies, calm down! It's a birthday party — let's not get too aggressive." Oh, go away, new boy. Don't break up the debate *now*. It was just starting to get interesting!
"Even if you're the birthday boy, Ethan, this isn't something I can back down on. Look, Phoebe. If Amara has her own reasons why she keeps you around, fine. I'll respect that. But why should you, someone who's been sheltered all their life by *mommy* and *daddy*, knowing nothing but their love, never having to worry about food or shelter or survival be here over someone like Leo?"
"I gotta say, Sera, I'm on Phoebe's side here. It's not *wrong* to be normal. In fact, I think that normal people are great! I love normal people. I feel like it'd be better if people *didn't* have to go through things just to have nice things, y'know?"
"Huh? That makes no sense. What about all of the people who *do* go through things? How is that fair? I disagree with my parents on a lot of things, but being grateful for what you have is something that we both share an opinion on."
"Why would you ever want to want to bring people down?"
"Amara?"
"I get trying to lift up people in pain, Sera. I do. But why would you ever want to push down happy people? I don't think that life is transactional like that. Phoebe and I equally don't deserve to get murdered."
"I agree. *Murder* might be a stretch. But it's not equal. Things should be equal. Why aren't they equal?"
"If you really want to have this conversation, why don't we have it in the room over there?"
"Only if everyone hears."
"Oh my god, Sera. Why are you such a drama queen?"
"Ex*cuse* me? How am *I* the drama queen? What about you? You chose to defend Phoebe by *yourself.*"
"Sera, I like you a lot, I really do — I might reconsider after this conversation though, depending on how it goes — but you gotta come back down from your outlandish opinions, girl."
"All of the people you've talked to, Ethan and me and Chelsea and Ken and the many, many more I'm sure I don't know, we're all really grateful that you're so understanding toward us. But — well, I don't want to assume, but for me personally — I don't want to be treated differently. I just want what happened to go away. I'd rather not talk about it, y'know?"
"Why not? It should be a badge of honour. You should totally share it! You'd be way cooler to other people. Dark and brooding. *So* interesting."
"…Is this how you thought the entire time?"
"Duh? I don't know why you're trying to stop me, Amara. You're all the way up there with so many more worldly experiences and perspectives, but I'm just here trying to get us lowly peasants up there so we can understand you better. It's hard to do that when you keep pushing us back down."
"Oh, Sera. Please tell me you're kidding. I didn't — there's no way you could possibly be *below* me. It's…it's not helpful. At all. All I want is to just be a normal person in a normal world. That's it. You don't need to be that person for me. Don't — don't hurt yourself doing that. It's not healthy. And it's never worth it."
"There is no such thing as normal in this world. Everyone has their perspective, their side, their story. Everyone is different."
"Oh, no. Trust me. There is *absolutely* a normal. And I strived for that for *so very long*, Sera. Way before I met you. Please trust me when I say that no one wants to experience trauma. It's *never* a good thing."
"You're telling me that what I've been through wasn't *real* trauma? When I asked my dad to shove my head underwater for fifteen seconds? When I asked George to tie me up against a board for a few hours? Drat. Maybe I should poison myself instead, that might have a stronger lasting impact…"
"You did *what??"*
"Oh, whew. You didn't know. Thank god. I thought it wasn't gonna count and I'd have to pull out all the stops to join the cool kids club."
"No, no, that's not what I meant. I — I think I might need to take a seat just to comprehend what exactly is going on. Maybe we could continue this conversation later."
"Yeah, yeah, no problem. By the looks of it, I might not be at your level, Amara, do you think I've suffered enough to beat Ethan, maybe? Maybe an Ethan and a half?"
"I…I can't. I'm sorry. I have to go."
"Oh, shit, Amara. I'm sorry. Did I trigger your latent trauma?"
"No, no, please — please leave me alone for a moment. Please."
"Ooh. Gotcha. Lemme know if you level up again!"
"Yo, Sera, what'd you do to Amara to make her run out like that?"
"She taught me that I have a lot of room to improve. Not as much as you, Phoebe, but there is still a great distance between me and her."
"Er… That's not *bad*, I think. So why was making an expression like she wanted to vomit?"
"Oh, y'know. People with checkered pasts often relapse into it, they get sad after something reminds them of it, and it's a true journey and a clear example of humans thriving through adversity that lets them overcome it. I'm really impressed by how she handled it, myself."
"Huh. There's…a lot to unpack here. Um."
All of these humans are so strange. They bear such strange expressions toward each other, as if they cannot understand each other. Bluebirds do not have this problem at all. Why don't they stare at each other and immediately comprehend what the other is thinking?
Or fight it out. That's always a clean and decisive way to determine any winners or losers. Hell, if I could, I'd fight on Sera's behalf myself. I've never met a human who so clearly understood our perspective.
When you grow up, go adventuring more. See the world. See its people, face its challenges, and suffer until you can experience the full range of bluebird emotion. All of these pussycat humans are cowards. Probably never worked a single day in their life. Never had to forage for food in the winter, or watch another human die.
To hurt is to live. How can one possibly know happiness if they are never sad? How can one possibly know pleasure if they are never in pain? These humans will never reach the heights of emotion, never achieve their true potential.
"Sera, I'm on your side!"
Ah, another sane human.
"Eh. Lucas, you don't count. You're not even at my level, let alone *them*. Glad you have the right mindset though. If you listen to me, you'll be able to understand the world more. Understand people more. And how could that possibly be a bad thing? I simply want people to broaden their perspective."
The other girl returns.
"Sera, could we talk about this later? It's Ethan's special day, and sometimes it seems like you talk about nothing but how people would be doing so much better if they just stabbed themselves sometimes."
"But they should! It'd be a learning experience."
"Sera…"
"Oh, right. Fine. Ugh, what a spoilsport. Right, guys?"
Hey. These are Sera's friends, aren't they? Why aren't they sticking up for her? Just because they share an objectively incorrect opinion doesn't mean that they can dismiss their friend like that. How rude.
---
"How was the birthday party?"
"Not great. Some sheltered rich girl was there and she was being a jerk. That kinda ruined the whole thing."
"You should be grateful that you even have the opportunity to go to such a frivolous event. When I was your age, I was already married and working, not fooling around and spending lavishly on snacks and treats. Consider yourself lucky."
"You keep telling me over and over again. I *get* it, ma. Unlike some other people."
"Oh, sweetie. I know. There's not much you can do about it if they really don't want to change. You have to be willing to look at the flaws inside you, and it seems like these people just don't want to do that. It's a shame that there are so many like them out in the world."
"I told one of them that they should broaden their perspective and be more considerate, and she almost punched me!"
"Hmph. Ungrateful rats. Their parents must have been too protective of them. Didn't want them to get their hands dirty in the real world. Probably only went to the *sanitised* plastic playgrounds. No, they were the ones *sanitising* the playgrounds."
"They gotta be."
---
Sialia, I know that *you* never wanted kids, but Mr. Swainson, I cannot believe you. I thought that as a fine, distinguished gentleman, you would have had the orthodox mentality that one must experience as many things in life as possible. This is an impossibility.
"Elizabeth, let me be frank. I have seen enough of the world, and I would rather my children not have to see the parts that they do not need to see. There are some things better left unsaid."
You are much too protective.
"There are things that birds *should not* experience. It would be incredibly traumatic and have a terrible effect on their mental health."
Mental health? What are you talking about? Birds can function perfectly fine no matter what Garuda throws at them. It's how we thrive as a species. Why, my eldest daughter flew straight into a travelling car's windshield and she values her life all the more for it. I myself tried the same — with a slower car, to minimise the risk of injury — and found myself with a greater appreciation with the world.
"Why would you do that to yourself?"
To understand her and the experience. How could you be so aghast about this?
"Liz, you're an affront to all that pain stands for. Things hurt for a reason."
If one never hurts, how can one love? Emotions are relative. A bird with the emotional range as wide as a feather will never feel true, unadulterated joy. Birds — and humans for that matter — have to hurt sometimes.
That is what pain is — a reflection of happiness. Pain *helps* us feel happy, Sialia, Mr. Swainson. It is a very good thing.
"Elizabeth. Let me ask you this. I understand that it may be difficult to change your mind —"
Absolutely.
"— but if you could put yourself in a situation where you would never hurt or feel sad again, would you?"
No! Didn't you hear a single word I said?
"Liz, I almost forgot to bring this up because of all the other crazy philosophical stuff you went on, but let me remind you that Juliette is *not okay.* I have no doubt that if you asked her, she would agree in a heartbeat to not have had her beak crushed in. She didn't and still doesn't *want* that memory, Liz. Pain is something Garuda blessed us with so that we know what to avoid."
She can absolutely have memories that she'd rather not have but still are better for her in the long run. Children never want to do work around the nest, but you would still encourage them to do that, wouldn't you?
"That's what I'm trying to say. It's *not* better for her in the long run. I can't believe I have to explain to you why making your children do chores is not at all comparable to She's deathly afraid of any moving object now. Don't you know that that's why she stopped human-watching with you? Why she avoids flying? Why she moved to Florida? She's so afraid that she never wants to experience anything like that again. She barely talks to the other birds, Liz. When was the last time you visited her?"
I… It must have been nearly three years now. I was not aware of this. Regardless, if she tries harder, she can overcome any obstacle. Give it some more time and she will surely rise up to the occasion and rise through the ashes like a phoenix to her new self. Stronger than ever before.
"Help me out here, William. She's not on my wavelength. Got any solid arguments to stop her from putting through Archie here through some sort of torture machine later on in his life? I wasn't worried before, but now I definitely am."
"How many years have you been human-watching, Elizabeth?"
Almost ten. Why?
"And in those ten years, how many humans have you seen that were hurt?"
Quite a few. Why?
"Most humans who are hurt don't express it. To my understand, instead, they actively try to hide it to reintegrate with society. There are a lot of them — both humans and bluebirds — that aren't able to fully. Maybe an equal number that aren't able to at all."
Suppose you're right. These people are harder to see in the world. But —
"Maybe we should drop this topic. Clearly we aren't getting anywhere. William, I'm sure you must want to get home by now. We've kept you for far too long. I'll stay with Liz, don't worry. At least until we reach the great mountain range. Help her keep an eye on Archie, too."
"My thanks for your companionship the way here."
No, no, the honour is all ours! Sialia and I, we've loved having you here. You've provided such a great perspective that we've learned a lot from. Perhaps in the future we could meet up and have more…insightful discussions in the future. I'd love to visit you in Montreal.

View File

@ -0,0 +1,286 @@
---
title: "Illusion of Solitude"
date: 2023-12-29
tags:
- birdseye
- nanowrimo
- featured
---
**Summary:** An old bluebird takes her grandson human-watching in Vancouver. This time, they find a young child dealing with grown-ups who just don't understand.
<!-- more -->
---
Keep to the left. Mind the seagulls. We're approaching the coast. Those bastards appear far too often. One of the only reasons why there isn't a permanent bluebird population in Vancouver.
We've almost reached our destination. I suppose we could fit _one_ more experiment here. Reach out to one last human. For this last one, how about you pick? You've seen me do it a couple of times. If you need assistance, I'll be right here.
That building? That could be anything. If I were to guess, judging by the number of residential buildings in the area, that might be a school. Good timing. School should be in session by this time in the morning.
Ah, these are younger children. Perhaps no older than two hundred times your age. An excellent choice, any of these. I don't believe we've watched any humans so young. It'll be more difficult, though. Usually you see their parents hovering around them when they're so young. Perhaps not at school, but once outside the compound, humans become fiercely protective of their young. Like no other species I have ever seen.
These particular children over here playing outside. Mind the adult. If he sees you, he'll chase you away and keep a more careful eye out. They must be in recess from learning.
Which one do you like the most? The one talking to the wall in the corner away from all of the other children? You have good taste.
"Zoe, do you want a cup of tea? Here. I made it nice and cold for you. Do you like it?"
She is truly talking to the wall with nothing in her hands. Is the wall named Zoe?
"Ruth, are you talking to nothing again? Why don't you play with the rest of the kids?"
"Mrs. Pernumble, I'm having a tea party with Zoe! And _you're_ not invited. So go away."
"Right. Zoe. Your…"
"My friend. She's waving at you. Oh — be careful, Zoe! You'll spill the tea if you wave your hand like that."
I do not see a child waving with a cup of tea.
"I…see." As a trained expert in recognising human patterns and mannerisms, I, Elizabeth von Turdidae, can attest that the woman is lying. Kindly lying, but lying nonetheless.
"Surely Zoe wouldn't mind if you spent a few recesses playing with Jeremy and Sheila. Doesn't it sound fun to play soccer? They're all laughing in the field. It sounds to me like they're having the time of their lives!"
Children are such mysterious creatures. This one might be staring daggers into the woman. "I can't leave Zoe here. She doesn't like soccer. And she gets scared without me. And I haven't finished my cup of tea!"
I would like to re-attest that there is, in fact, a lack of any tea or anything resembling tea in the immediate vicinity.
"I could keep Zoe company. She's got a lot of questions for me, right? So perhaps we could have a conversation while you play soccer."
"I don't think so, Mrs. Pernumble."
"Why not?"
"Because you haven't said a word to her and now she's very upset. It's okay, Zoe. I'll never leave you. Have another cup of tea."
I know that I'm getting along in years, but I _do not_ see a human child. Several worm children that look very tasty, yes, but no human child. Or tea. Do you see a human child?
Where is it?
Interesting. Perhaps this is a special human child. I have not known any human child that could hide themselves from other people, but humans still have plenty of surprises in them yet.
…But where's the tea?
"Oh, Ruth. Please. I'm sure the other kids would love to play with you. Can I tell you a little secret?"
"La la la I can't hear you. Can you hear a Mrs. Pernumble talking, Zoe? Yeah, me neither. Oh, thank you for the biscuit! Let me refill your tea again. You're a fast drinker!"
Heh. If there's one thing that is common between humans and bluebirds, children exasperating elders is universal across species.
Although I certainly wouldn't sigh and walk away like her. No, misbehaving children must be punished so they don't do it again.
"Thank you for the biscuit, Zoe. Have you heard from Miss Piggy recently? I heard that Miss Froggy and her had the most delicious of teas the other day. Mm. You're right. That _is_ very interesting."
I must admit, it is rather strange to watch a conversation happening with a human I cannot perceive in any way. I hope you're learning more than I am.
"Um, Ruth?"
That sigh does not belong on a child her age. How adorable — perhaps she thinks that if she's as petulant as one, others will see her as an adult.
"I'm _so_ sorry, Zoe. I know you don't like it when our tea parties get interrupted. What _is_ it, Peter?"
"Who are you talking to?"
"Zoe, of course. My best friend. She says hi, by the way. Now, get to the point. What do you want?"
Ha! Another victim of Zoe's invisibility! He looks so confused — oh, my sides.
"Mrs. Pernumble said that maybe you wanted to play with me and Alfred over there? We're playing cards."
"Oh. She — um. I… I can't. Zoe needs me to keep her company. She doesn't like it when I leave her alone."
"Who? I don't see her."
"I _told_ you, Zoe's my best friend. It's not _her_ fault that you can't see her."
That hesitant wave — the worm on top! Glorious. I might have to start observing the younger humans more. They're such a racket! Two inexperienced creatures learning about the world.
"Are you sure she's real?"
"Of _course_ she's real! Say sorry to Zoe, right now! I can't believe you'd say that. I'm sorry, Zoe. He didn't mean it. He just doesn't know better. Right?"
"Uh, I'm sorry!"
"Zoe's over _here_. Look at her properly when you say sorry."
"…Sorry."
"Hmph. She says she forgives you this time."
"If she wants, she can play with us too?"
"Nah. Zoe doesn't like cards."
"Okay then. If she doesn't want to. Bye bye."
"Bye bye."
My scientific mind is racing at the possibilities. The child couldn't see her. The human couldn't see her. I can't see her. What are the criteria for who can and cannot see Zoe?
---
"Be sure to finish the letters worksheet — I expect to see perfect 'A's from everyone! Now, children, fetch your bags — it's time to go home!"
The chatter of children after school is the same regardless of their age. It's always so relaxing to immerse oneself in the wave of conversations taking place left and right.
"Sheila, Sheila, are you still coming to my house after school?"
"I got this super cool fire truck for my birthday! Leo, you gotta bring your cars!"
"Nooo! My mom grounded me. You have to beat the boss without me, Dylan! You have to!"
"Come on, everyone. Less talking, more moving! Once you've changed into your outdoor shoes, line up against the wall outside the classroom!"
---
Well done, child. You're following your humans quite well. I barely had to help you at all.
"How was school, honey? Did you learn a lot of new things?"
"We learned about the alphabet! I can draw lots of little 'A's now. Today we learned about the big 'A'."
"Hey! It's my favourite kiddo! Ruth! Ruthie. Beat up any boys today?"
"Zoe almost. Hmph. Peter was being mean to Zoe. I feel so bad for Zoe, daddy. No one understands her and they're all so…so mean to her!"
"There's cookies and juice for you on the dining table, Ruth. Make sure to wash your hands."
"Aw, that sucks. Does Zoe want a glass of orange juice, too? Maybe it'll help her feel better?"
"Daddy! Zoe is _allergic_ to orange juice, remember? I told you _yesterday!_ She'll have her unicorn hair tea like she always does. Mommy, can I eat my cookies upstairs? Me and Zoe are having a tea party again."
"All right, but don't spill any of the crumbs! We don't want ants moving into our house, don't we?"
Oh! How unfortunate. Child, I must interject — allergies are new to you, yes? Bluebirds don't particularly have the horrid disease. If some humans — and it's impossible to tell which humans before they actually eat it — eat certain foods, like peanuts! Or fruits, or nearly anything — they could _die_. That's called an allergic reaction.
To have an allergy toward a fruit — truly a pitiable existence it must be.
"Tea party time! Zoe, here is your unicorn hair tea. Freshly stirred! Only the best for my best friend. Oh, thank you! Yes, I'm doing great. Can you help me with my homework after this? You're always so good at drawing those weird lines. Mrs. Prenumble says that they're part of letters, which are part of words.
"Zoe… Why does everyone hate you so much? They never want to talk to you. Even Mrs. Prenumble. They all pretend that you're not a _real_ person. But don't worry. _I_ know that you're real. No matter what everyone else says. You're my best friend, right? I can't leave you alone. If you want, I'll stay with you, forever and ever!
"Will you also stay with me, forever and ever?"
Human children. So adorable! Already forming pacts for life in the tender young stage of pre-adolescence.
---
Finally some change. Pardon me, but I can only listen to a child have a multi-hour tea party go on for so long before even _my_ patience wanes. Let us investigate the commotion at the door. Ooh. An older boy. A family! Human families are so small. Their babies don't die nearly as often as bluebird babies, but there are just so few of them in the world. I suppose that explains the parents' protectiveness. But if they want their children to succeed, why don't they simply have more children? That would maximise the probability of successful children reproducing in the future.
"Mom, dad, I'm home."
"Welcome home, Jacob! Dinner will be ready in just a minute. Could you call Ruth downstairs from her room?"
"Hey, kiddo! How was school? Beat up any girls today?"
"Oh my god, you can't _do_ that, dad!"
"Hmph. All these fancy schmancy new norms are stifling. Back in my day, you could hit whoever you wanted, and the other guy would just hit you back! None of this 'no hitting girls' nonsense."
I knew that humans discriminated between their sexes, but I didn't realise that they couldn't hit females. All kerfuffle and waffle, if you ask me. If someone has wronged you in any way, giving them a good knock in the old noggin is always an excellent first move. Really sets the atmosphere for a full-out fight.
"They're weaker, so it's not fair. They can't hit back, so it's up to us men to show other men who's boss."
Nonsense! Excuse _me?_ I don't know about _humans_, but female _bluebirds_ pack a pretty punch in their wings. It's true that males usually only fight males and females usually only fight females — because, let's be honest, why would a female ever need to fight a male? — but I did not realise that there was _no_ cross-sex combat in human world. Strange, too. I could have sworn that I'd seen human males fighting human females multiple times over my many years.
"Son, maybe the times are changing. And I respect that. But no matter what happens, never lose that fighting spirit. Can't fight the girls? Fine. There's still a good half of the population you can beat some sense into. Now, if they say that _fighting is bad_ and that _no one should do it_ or whatever? That's when you put your fist down and screw the rules. Let no one tell you what you can or can't do."
"I'll do my best, dad!"
"I know you will, son. Make me proud. Nothing like a good fist to the noggin to help loosen any lips."
Wonderful parenting. _This_ I can get behind one hundred percent. I know I have a lot of negative things to say about humans, but in reality, they're so diverse that there are so many different viewpoints to learn from. There are some that are objectively wrong, some that are objectively right, and then most are in the middle. It's rather refreshing to have one's opinions validated every once in a while.
And what camaraderie! Certainly bluebird children are never so close to their parents. Or their grandparents, for that matter.
"Now bring Ruth downstairs like your mom said, will you?"
"Yessir! Ruth!"
"You might have to go upstairs to bring her down. I think she said that she was having a tea party with Zoe again."
"Zoe? Ugh. How long are we gonna entertain her, dad?"
"As long as she wants."
"Hmph. Ruth! Open up! Mom says it's time for dinner!"
I wonder why she isn't responding. Drat! If only we could be invisible and incorporeal like Zoe must be, so we could position ourselves _in_ the door and see both sides of the conversation. Having to move around to the back of the house just to see the hallway is _so_ irritating.
"Ruth! If you don't say anything, I'm coming in in three…two…"
"I'm having a tea party! Go away, Jacob."
My. She sounds aggravated. What kind of dirt landed on her head while we were away?
"I'm not gonna wait here for you to finish your prissy tea party. I'm comin' in!"
This is actually quite nice. It means if we circle around to the other side of the house, we should get a nice and proper view of everything that is happening.
"HEY! I didn't say you could come in! Zoe doesn't like you, you know. She says that you're big and fat and dumb and stupid for interrupting her tea party. Get out."
"Oi. I just came to pass on a message. Why do you have to get so pissy?"
"This is _my_ room." How funny. Ruth trying to push Jacob out of her room. Certainly it isn't happening unless it's under his own volition. Valiant effort, though. I would applaud like the humans do if I had hands. "Can't you read the sign? It says NO JACOBS!"
"Do you even know how to read?"
"Shut _up!_ Zoe said that you can't be here, and that means you can't be here! Zoe needs her time to herself. She's has a very tiring day and she needs her tea and biscuits —"
"Zoe this and Zoe that. Jeez, don't you have any _real_ people to back you up?"
Oh. That one might have hit her harder than him slamming the door open. What is that expression on her face, I wonder?
"Yeah? Don't have anything to say, dontcha? I wonder why?"
"Say sorry to Zoe right now. She's my _best friend."_
"Oh my god, Ruth. How are you still so stuck on her? Zoe's not real! She's in your _mind!_ She's one of those _imaginary friends!"_
Certainly not. I find it rather unlikely that the human adults and the human children spoke to her if she wasn't real. That Mrs. Pernumble or whatever it was wanted to have a conversation with her, too.
However, judging by the blankness of Ruth's face, she might believe him.
"No."
"Yes." Rather gleeful of him. "You're _imagining_ her. Probably because no one likes you enough for you to have _real_ friends."
"That's not true. Shut _up._"
"Oh, yeah? What's her favourite colour?"
"Yellow!"
"What's her favourite food?"
"Unicorn hair tea with rainbow biscuits!"
An eyebrow raised. In his defense, I didn't think that those were real either. But it makes sense to me. What else would an invisible person eat? Oh! It just clicked for me. That must be _why_ she's invisible! All of these exotic ingredients must have an effect on the body that makes her difficult to perceive. And Ruth must be consuming enough that she can perceive her but not turn invisible herself.
My cleverness and intelligence scare me sometimes. But I digress.
"What's her last name?"
"It's! Um…"
"Where does she live?"
"I… She must have told me…" A fearful glance to her right. Is she losing the fight? Come on, how difficult could it be to prove that a human exists?
"Exactly. What does she do when she's not with you?"
"…Stop it…" Hm. Personally, I would prefer physical combat over verbal combat. Not nearly as emotionally messy. Much easier to know when the damage is done and when they've learned their lesson. Most importantly — much easier to restrain oneself. I suppose she's been thoroughly beaten. Her father will be proud — Jacob finally beat up a girl.
"When did you meet her?"
Oh?
"When grandmama went home! She's _real_, Jacob! I _know_ I met her. She was outside on the swing outside grandmama's house when the sun was setting and I was sad and we went on the swing together and we were friends ever since. Zoe's _real!"_ And here comes the crying. If she was sobbing before, she's bawling now.
Hm? Why isn't he saying anything else? Are tears so effective against physical and verbal combat? He seems shocked. How could this be surprising? Bluebirds meet new bluebirds all the time. Is this not true for humans?
"Jacob. Come over here, please." Oh, my. All of the tension in the air and I didn't even notice the mother in the doorway.
"Oh! Um. Mom, I can explain. I was just…"

View File

@ -0,0 +1,96 @@
---
title: "My Precious"
date: 2024-02-05
tags:
- shorts
- psychological
- featured
---
**Summary:** Max tries to save his dog, but it's not enough.
<!-- more -->
**A/N: we take some creative liberties re: symptoms of cancer / its rate of progression!**
The day you got your dog was the happiest day of your life. Your dog didn't yell at you whenever you delivered a pizza five minutes late. Your dog never complained when you didn't add enough sugar to her coffee. No matter how you complained over and over again how you weren't getting enough sleep, Susie was always there at the end of the day to greet you, a welcome bark and an expectant gaze ready for treats and a run. For every high and every low, your little bundle of joy was right by your side.
The day your dog became sick was the saddest day of your life.
The first signs were small: she didn't eat as much, and you noticed that she shed more fur than usual. But she was still the same playful golden retriever you knew.
Out of an abundance of caution, you decided to get her checked out at your local vet.
"There's a cancerous tumour at the base of her right lung." The vet shakes her head, her tone grave. You cast a worried glance at Susie's sleeping form on the examination table. "I'm sorry, Mr. Haltmann. You're lucky you caught it this early, but it's growing fast. My diagnosis is that it'll become terminal in a few months."
Your mouth dries. "What can I do?"
The vet taps her clipboard. "She'll need surgery. I can direct you to a place, but the cost can be upwards of five thousand dollars."
"Anything for Susie," you say, shaking your head. You're already crunching the numbers, but at the very least, you'll have to scrimp and save and start working a third job to save her. But it's worth it.
All of this is for Susie, the beloved dog who kept you company for the last four years.
---
"I suppose that that's settled, Mr. Haltmann. Welcome to Hotel Trivago. You'll be starting Monday."
"Thank you!" Your cheeks hurt from the grin you're restraining from spreading across your face, but you keep it professional long enough to shake your interviewer's hand. As you leave the building, there's a bounce in your step and you're humming that pop song you heard over the radio in your eleventh-hand car.
You trip over a bag of dog food and end up skidding to a stop before Susie's lethargic figure. "I promised you, Susie," you whisper in her ear. "I got a job. And that means that we're on our way there." You squeeze her paws. Susie lifts her head and tilts it at you, before she sets it down again, closing her eyes.
"Hang on just a little longer, please."
The hotel housekeeping work pays you $500, after which you'll be one-tenth of the way there. You haven't fallen over yet, although juggling four part-time jobs at once is still a struggle.
All of this is for Susie, your life and joy that kept you going for so long. You can't wait until the day you can play fetch with her once again.
---
It's not enough, you realise. You need more. You read online that taking care of yourself was most important, no matter what you might be going through, but you can't take your mind off of Susie. It's with a heavy heart that you make sure to feed yourself a nutritious meal every day while you know how much she's suffering.
A customer at the coffee shop you work at mentioned that tech is the place to be for veterans and newcomers alike, with plenty of jobs at many different skill levels. It'll be a time investment. Time that you might not have, but it's your best shot.
After you come home from your cashier gig at Walmart every evening, you go straight to the library and learn web development until closing time.
All of this is for Susie, your lovely little cat that needs a new leg. You can't wait to see her climbing up everywhere and gleefully shredding furniture with her claws once again.
---
"Already?" your manager says, her brow furrowed as she squints at your resignation papers. "You've only been working here for a year. You've been doing good work, too. Bradley hardly ever complains about your code."
"Sorry, Joyce. I got an offer from Google. They're paying me triple what you guys do, and I need the money." Even though you didn't get along too well with your colleagues, you feel a twinge of regret in your gut. These guys treated you well, and you learned so much. You almost feel ready for the big step.
She sighs, setting down the documents. "I understand. Software engineers," she muses. "They all come and go like the wind. Always chasing the rainbow. We're going to need you to document your work these last two weeks. Bradley'll take point for the knowledge transfer."
"I appreciate it, Joyce." Internally, you let out a sigh of relief. She took it better than you'd expected. You thought the crabby woman would have tried to hold you back. Hope blossoms in your chest for the future as you make your way up in the world.
All of this is for Susie, your estranged daughter who left you until you could fix your life. You can't wait to see her expression when she sees who you've become.
---
She doesn't move. You stare ever more intently at her still form, waiting. It's all out of your hands now. If your calculations were correct, then…
"Mr. Haltmann, sir! Mr. Haltmann! I have urgent news!" Your office door clatters open. The glass walls around you let the midday sun shine into the hallway, illuminating a young man waving a sheaf of documents.
You chuckle. "Mr. Borparner, all news is urgent to the right ears. But do go on. What is it?"
"Their board of directors agreed! They're willing to sell you NVIDIA at market valuation, sir — I can't believe it!"
You lace your fingers together, resting them on the mahogany desk before you. "Excellent. Prepare a press release right away. I want the deal finalised before the day ends."
"Yes, sir! Right away, sir!"
The door slams shuts behind Borparner. You return to the chessboard sitting on your desk in front of you. The opposing queen wobbles, then falls over, knocking over the king in the process. "I suppose that that's over, then."
All of this is for Susie Inc, your heart and soul. You can't wait to see how her stock will jump once the trade opens tomorrow morning.
---
It's midday when you return to your empty house. You should buy a new house. Move somewhere nicer. This one holds a lot of sentimental value to you, but you can afford better now. You deserve better now. It holds a lot of sentimental value to you, but you can only fiddle with the keyhole so many times before you replace it. Day after day, the wasted time adds up.
A voice from the lawn next door calls out to you. "Max! Heya, neighbour. I heard you were raising money for your dog. How's she doing?"
You stare at her blankly. "What dog?"
The key finally clicks, letting you inside. Absently, you kick a persistently heavy towel away. It's starting to smell, too. How long has it been since you last did laundry? You tell your new servants to clean it up.

View File

@ -0,0 +1,134 @@
---
title: "I Found A Magical Fish While Fishing With My Cousin (In Another World)!"
date: 2022-11-14
_draft: true
tags:
- uoft
- nanowrimo
---
**Notes:** This was written as part of *University of Teyvat* for NaNoWriMo 2022, and also submitted to the WatSFiC × CWC Short Story Contest.
---
A wooden rowboat drifted over a lake. Its two occupants sat facing each other on their respective benches, wearing matching wide brim hats as they relaxed in the bright summer sun. Two fishing lines were mounted on opposite ends of the boat, completely still in the water.
<!-- more -->
All was quiet but for the light brush of wind ruffling their clothing, and the water gently lapping against the side of the boat. Yanfei tipped her head back, adjusting her cap so that it would sit over her eyes. "Thanks again for coming out with me, Ganyu. How's work, by the way?"
"Oh, it's light right now, don't worry," Glancing at the rods floating around the water, Ganyu waved the matter aside. "I should be asking about you. I hear your legal consultancy has gotten a lot of attention lately."
"Yup! Although," Yanfei sighed, "there's so much conflict between Vision-holders and the common folk lately. So many people are suing each other left and right… The divorce cases are the *worst.* No one's ever always right, and *I* have to be the one to…to… I wish society was more black and white," she said, slumping further back against the wall.
Ganyu chuckled, her eyes darkening. "You don't say."
"You've lived a lot longer than I have." Yanfei glanced up at Ganyu. "What do you think?"
"Hmm…" Ganyu said. "Do you know an old book from Mondstadt written by wind-worshippers?
"*Lost Prayers to the Sacred Winds*, right? It's supposed to be insightful — I've been meaning to check it out sometime. I wish I had one, though."
"You should give it a read. It's not going to help you very much with dealing with people, but it's still a good book. Personally, I think experience is what helped me the most. Visit new countries, meet new people, throw yourself into very different situations…" Her voice trailed off as she stared into the depths of the lake. The murky water made it hard to see past a few centimetres, but the slight wiggling of the line sent unmistakable ripples through the previously still water. "Yanfei, do you see that?"
"We got something!" Yanfei grunted, pulling hard on the fishing rod as she tried to reel it back in. "I…think this one's…a big one! Ganyu…help?"
Ganyu reached over and effortlessly *yanked* the fishing line up with her hands. A mossy treasure chest flew out of the water and landed perfectly in the centre of the boat, splashing lake water all over their shoes. More water continuously streamed out of the chest and onto the base of the boat. Ganyu wrinkled her nose as she turned on the Automatic Magic Canoe Water Drainer™.
Yanfei stared. "How'd *that* get caught on the line?" She poked the chest using the other end of the fishing rod.
The chest did not poke back.
"It's kinda heavy," Yanfei reported. "Maybe it has treasure! I'm gonna open it."
"Are you sure? Something this old at the bottom of a lake would have a larger chance of being an evil creature that wants to eat your soul. Although," Ganyu mused, "if you're lucky, it could instead be some legendary magical weapon that has the power to freeze a whole ocean."
The rusty silver lock on the chest was broken, inviting the two to pry it open for the mystical contents inside. With a lowly, mysterious *creak*, Yanfei pushed open the lid of the chest. She and Ganyu stared inside. "Is that…a bow?"
Carefully, Ganyu removed the large, shining archery implement, avoiding the sharp blue spikes along its body. As she ran a hand over its aged wooden frame, Ganyu inspected the glowing runes all around the grip and belly of the bow, giving it an experimental flex and draw. "It says…Polar Star," she read, squinting to make out the ancient language. "Seems like a good bow. I'll keep it."
"Good decision," Yanfei nodded. "If you don't overflow on CRIT Rate, it's your best-in-slot in freeze teams. Second BiS in melt, too."
"Huh?" Ganyu asked.
"Huh?" Yanfei asked. "I mean, you're the archer girl, not me. I'm not gonna be able to use it. It's all yours."
Turning the bow over one last time, Ganyu swung the bow behind her. Yanfei watched it disappear into pretty golden sparks into the secret dimensional subspace where all of them kept their weapons. "I think there was something else in the chest."
Indeed, they heard a *flop-flop-flop* coming from the chest. There was still one more item in the chest. Something…alive.
Yanfei creeped closer, eventually poking her head over the lip of the chest to peek inside.
An orange fish with bulging eyes and a mouth that looked like it was used to sucking bananas all day greeted her. Yanfei gaped. "It's so cute! Is this a koi fish, Ganyu? It's orange, so that means it has pyro powers, right? Oh, I'll name you…magic koi — Magikoi!"
"Actually, I think it's a different kind of carp."
"Oh. Magikarp, then. Come with me! I'll take care of you. You're going to grow up to be big and strong one day," Yanfei said gently, a warm smile on her face as she reached out with both hands to welcome the fish into her extended family.
Evidently, the fish did not want to be a new member of Yanfei's family and *splashed* out of the chest right onto her face.
*"Blergh!* Get it off, get it off!" Yanfei batted the fish away as she turned her head out to spit fish water, *eugh*, out of her mouth. The fish went flying toward Ganyu, who dodged it instinctively.
"It's getting away! No, Magikarp, stop!"
In one rapid movement, Ganyu drew an arrow from her quiver and fired, ice energy gathering at the arrowhead for a millisecond before she released it to trap the fish in a solid block of ice. It plummeted back to the bench beside where Yanfei was sitting.
Once she felt sufficiently less unclean, Yanfei proceeded to take out a worn music notebook and rip out one of its pages to set it on fire. The ice around the fish promptly melted and returned to reveal a fish. "It's not dead," Yanfei observed. "I think it's just fainted. That's one hardy fish."
Ganyu continued to aim the Polar Star at it. "Do you *want* it dead?"
Yanfei considered the idea. "Actually, do you mind holding off on that for now? I've always wanted to try something."
---
Ganyu and Yanfei stood on opposite ends of the small boat, which barely wobbled thanks to the Automatic Magic Boat Stabiliser™ 9000 that Ganyu activated. Yanfei thrust out her hand, kicking Magikarp out from beside her to take the field. "Go, Magikarp! I choose you!" she cried, waiting expectantly.
Like a fish out of water, Magikarp flopped onto the floor pathetically.
"Magikarp, you can do it!" Yanfei tried again.
Like someone imitating a dead fish, Magikarp floundered on the floor uselessly.
"Magikarp, I'm begging you! I'll give you snacks!" Yanfei pleaded.
Like a child throwing a temper tantrum because they didn't want to eat fish, Magikarp wiggled on the floor pitifully.
"I see. " Yanfei clenched her fist, staring down at the floor.
Like a Water-Type being electrocuted by an Electric-Type but not really caring, Magikarp jerked about on the floor woefully.
"I'm so sorry for not listening to you, partner," Yanfei whispered, falling to her knees.
Like a student just seeing the second page of the midterm paper for the first time, Magikarp drooped onto the floor anemically.
"It's my fault. I never treasured our bond enough," Yanfei said, squeezing her eyes shut. A single tear trailed down her cheek.
Like a joke that went on for far too long and was reanimated several times, Magikarp lurched about on the floor feebly.
Flames blazing around her to represent her determination, Yanfei fist-pumped the air, gesturing wildly before pointing at Ganyu. "I understand you now. Magikarp, *use Splash!"*
Like *it was the weakest and most pathetic* fish in the world, Magikarp splashed about on the floor aimlessly!
But nothing happened. Yanfei stood in silence for a few seconds. Ganyu returned from her fighting stance to a normal standing position, frowning. "How useless."
Yanfei nodded agreeably. "Yeah, Magikarp kinda sucks. The only thing it does is splash around. Magikarp," she declared, "you have disappointed me for the last time. By the power invested in me by myself, I sentence you…to exile!"
Ganyu pointed her bow at Magikarp.
"Wait! Do it outside the boat." Yanfei tossed Magikarp off the edge of the boat. Before it could land in the water, the trail of a fully charged ice arrow *schoom*ed past her and slammed into the fish, blasting it straight up. The fish comet left behind a beautiful trail of white that vanished as quickly as it appeared. "Nice shot."
"Thanks."
The two shielded their eyes, watching and waiting for the fish to come back down. "That might be a little bit farther than exile," Yanfei commented after a while.
"It's in a whole new world now," Ganyu said. "Nothing we can do about that."
"Nope. Not a thing." Yanfei settled back on her seat, lounging as they waited for another fish to bite the hook. Snowflakes gently fluttered down around them, melting and sending ripples where they landed in the water. A slight fishy scent filled the air.
"Snow in July," Ganyu observed. "That's new."
Yanfei glanced up. "There's not a cloud in the sky, either."
"Climate change, huh?"
"It's gotta be," Yanfei agreed.

View File

@ -0,0 +1,38 @@
---
title: "Transition to Universe City"
date: 2022-10-12
tags:
- barin
---
Porter Hall\
Universe City, Leeco
Brian Wright\
Chancellor\
10077 Seascout Avenue\
Cekendery, Leeco
<!-- more -->
Dear Chancellor Wright,
Leeco's Board of Directors convened last night and have appointed me to share with you
their decision regarding the expansion of Universe City. Token resistance from the district
boards were swiftly overturned with immense support from the populace.
Universe City, the "second capital" of Leeco has grown tremendously in cultural and
economic value to the nation over the past several years. Effective 1 October 2022,
Universe City will be designated the official capital city of Leeco. District boards have
already been instructed to begin the process of migrating, and Administration are roughly
one-third of the way done the transition.
Because the decision was made unanimously, consider it impossible to revoke the will of
the Board. Cekendery must maintain good standing with the Board during the transitory
period.
I wish you the best of luck in maintaining unity.
Your Studious Pupil,\
Ira Hagey\
Vice Chancellor of Leeco\
28 September 2022

View File

@ -0,0 +1,60 @@
---
title: "A Triden(t) Against the World"
date: 2024-02-29
_draft: true
tags:
- shorts
- "content warning: political fluff"
- horror
- u.s. politics
---
**Disclaimer: This was mentally painful to write and I hope equally repulsive to read.**
"Is it yet another left-wing liberal conspiracy, or has President Trump finally taken back control of the White House? His unannounced visit is a show of strength against the authoritarian measures the Biden administration has imposed upon us all."
<!-- more -->
---
"President Biden shows his patience in entertaining former President Trump upon his unauthorised entry to the White House. Reliable sources tell us that the two are discussing the change and continuity in their policies toward China."
---
Two men stared at each other from opposite sides of the Oval Office.
"Donald." The man at the desk stood, moving to the front of the desk.
"Joe," the other man said coolly. "It's been a while."
A pause.
"Darn right it has!" Joe grinned. "Come here, Donald!" he said, spreading his arms. "I can't believe it's really you!"
It took just three large strides before Donald was wrapping his arms around Joe, laughing. They held each other for nearly a minute, doing nothing but basking in the other's touch after so long.
Eventually, Joe pulled back, leaving his hands on Donald's shoulders, staring proudly into his eyes. Donald was the first to break eye contact. "I see you haven't changed the room much," he said. "Why don't you tell me what you've been up to?" He sat and patted the cushion beside him.
Joe chuckled, capitulating by plopping down by the armrest. "Two years in and you're acting like you own the place," he said, scooching over to press closer to Donald.
Donald waggled his bushy blonde eyebrows. "I *did* own the place."
Joe's face shadowed, and Donald pulled him closer when he noticed. "I missed you. The real you. You don't have to keep it up anymore, you know." Joe shook his head. "You've done it. It's over."
"Eight years," Donald said solemnly. "I promised that I would give you eight years. I'll run in 2024. It'll anger half the country just enough to let you clinch another term."
"You don't have to," Joe argued. "Kamala already said that she'd find someone else to be VP. I know how much effort you've put in for me already."
"Oh, my little robin." said Donald affectionately, patting Joe's head. "You know that's not going to be enough to convince them."
Joe made a face at the pet name, but he leaned into the touch all the same. "You know I hate it when you call me that," he groaned. A smile tugged at his lips nonetheless. "I wish we could be like this forever."
"I wish we could, too." Donald sighed forlornly. He ran his fingers through Joe's white locks. "But you know what the media's like. They're already on my case about today. By the way, are you using a new shampoo?"
"You noticed?" Joe said, pleased. "I smelled it off of this high schooler from Iowa. It reminded me of you. What do you think?"
"Keep it," Donald decided. "The chamomile smells so soothing."
They sat there, leaning against each other, simply enjoying the shared warmth of the other's presence. Finally, Joe spoke up. "Want to come up with a cover story together?"
Donald rubbed his hands together. "You're on. I'm thinking of asking Pingping to cover for us with a trade deal or two…"

View File

@ -10,14 +10,14 @@ Imperial Palace\
1 Kansei Road\ 1 Kansei Road\
Emina, Asvyn Emina, Asvyn
<!-- more -->
Mr. Brendan May\ Mr. Brendan May\
Chairman\ Chairman\
Enigma Alliance\ Enigma Alliance\
3 Indigo Boulevard\ 3 Indigo Boulevard\
Saiyu, Weilam Saiyu, Weilam
<!-- more -->
This is to inform the Chairman, in accordance with the Enigma Agreement, adopted at Saiyu on 12 December 1915 ("the Agreement"), that the Asvish Empire intends to exercise its right to withdraw from the Enigma Alliance. Unless the Asvish Empire identifies suitable terms for re-engagement, the Asvish Empire will submit to the Chairman, as per Article 50, paragraph 2 of the Agreement, formal written notification of its withdrawal as soon as it is eligible to do so. Pending the submission of that notification, in the interest of transparency for parties to the Agreement, the Asvish Empire requests that the Chairman inform the parties to the Agreement and the States entitled to become parties to the Agreement of this communication relating to the Agreement. This is to inform the Chairman, in accordance with the Enigma Agreement, adopted at Saiyu on 12 December 1915 ("the Agreement"), that the Asvish Empire intends to exercise its right to withdraw from the Enigma Alliance. Unless the Asvish Empire identifies suitable terms for re-engagement, the Asvish Empire will submit to the Chairman, as per Article 50, paragraph 2 of the Agreement, formal written notification of its withdrawal as soon as it is eligible to do so. Pending the submission of that notification, in the interest of transparency for parties to the Agreement, the Asvish Empire requests that the Chairman inform the parties to the Agreement and the States entitled to become parties to the Agreement of this communication relating to the Agreement.
Signed,\ Signed,\

View File

@ -0,0 +1,41 @@
---
title: "Bienvenue au Ciers!"
date: 2020-06-17
tags:
- barin
- unstagnation
---
_“Vous êtes maintenant à la station dEscribe. Cest la dernière station de la ligne de train CiersXunil._
_“Les portes vont ouvrir: à gauche._
_“Veuillez vous tenir à lécart des portes.”_
<!-- more -->
---
Bienvenue au Ciers! Veuillez donc vous assurer que vous avez lune des documents suivantes:
- Si vous êtes un résident permanent ou un citoyen de Ciers, vous devez avoir votre étiquette didentification de Ciers.
- Autrement, si vous êtes un citoyen dune autre tribu dans le zone de voyage de Farele, vous devez avoir identification valide de votre gouvernement de votre tribu. Ceci peut inclure:
- La licence étudiant de Leeco
- La licence professeur de Leeco
- La licence Non de Leeco
- Le billet intertribal de Demauge
- La carte de transports Farele de Xunil
- Autrement, si vous êtes un citoyen dune tribu de lAlliance Énigma, vous devez avoir le document de voyage FE1-D vous issue par votre tribu.
- Autrement, si vous êtes un citoyen dune tribu de lAlliance Preton, vous devez être accompagné par un fonctionnaire de votre tribu.
- Autrement, si vous êtes du continent de Barin ou tout ses îles (Voir le document de voyage 6-B pour une list complète des îles), vous devez remplir le document de voyage 6-C avant vous allez aux services frontières.
- Autrement, vous devez remplir le document de voyage 6-D avant vous allez aux services frontières.
Malheureusement, à cause de létat actuel des choses intertribal, les passeports intertribaux ne sont pas acceptées comme identification. Nous nous excusons pour tout inconvénients.
Merci pour votre coopération!

View File

@ -8,10 +8,10 @@ tags:
A huge uproar erupts from the crowds of Saiyu as the results of the 2020 Weilamese election are revealed to the world. A huge uproar erupts from the crowds of Saiyu as the results of the 2020 Weilamese election are revealed to the world.
<!-- more -->
"…and your new President of Weilam until 2024 is…Roy Tamino!" "…and your new President of Weilam until 2024 is…Roy Tamino!"
<!-- more -->
"What the hell!" One citizen has his mouth hanging open. "How—how did *he* win!" "What the hell!" One citizen has his mouth hanging open. "How—how did *he* win!"
A few metres away, another is waving a campaign poster in the air, cheering wildly for her preferred candidate. "Woo! For Weilam! Young power!" A few metres away, another is waving a campaign poster in the air, cheering wildly for her preferred candidate. "Woo! For Weilam! Young power!"

View File

@ -4,6 +4,7 @@ date: 2020-06-25
tags: tags:
- barin - barin
- unstagnation - unstagnation
- featured
--- ---
It is — unethical and *deeply* immoral to block our people from knowledge. Is Leeco not a free tribe? Do we not accept those in pursuit of information? We cannot block our citizens from learning more about the universe to further the human race. Our tribe was founded on the principle of helping each other learn and grow by education. We cannot learn if we never challenge our beliefs, no matter how deep their roots lie — you might remember how ingrained racism was in Leeco so many decades back — and we cannot grow as a society if we resist change! If we do not rapidly adapt to the world, the world will rapidly adapt around us. And that is unacceptable. It is — unethical and *deeply* immoral to block our people from knowledge. Is Leeco not a free tribe? Do we not accept those in pursuit of information? We cannot block our citizens from learning more about the universe to further the human race. Our tribe was founded on the principle of helping each other learn and grow by education. We cannot learn if we never challenge our beliefs, no matter how deep their roots lie — you might remember how ingrained racism was in Leeco so many decades back — and we cannot grow as a society if we resist change! If we do not rapidly adapt to the world, the world will rapidly adapt around us. And that is unacceptable.

View File

@ -4,6 +4,7 @@ date: 2020-06-19
tags: tags:
- barin - barin
- unstagnation - unstagnation
- featured
--- ---
*Test 19/20 failed: Memory access violation.* *Test 19/20 failed: Memory access violation.*

View File

@ -0,0 +1,41 @@
---
title: "Welcome to Ciers!"
date: 2020-06-14
tags:
- barin
- unstagnation
---
“_You are now at: Escribe Station. This is the last stop on the CiersXunil Line._
_“The doors will be opening on the: left side._
_“Please stand clear of the doors.”_
<!-- more -->
---
Welcome to Ciers! Please ensure that you have at least one of the following before you leave the train:
- If you are a permanent resident or citizen of Ciers, or if you are a tribal representative with a Ciersian Identification Tag, you must have your Ciersian Identification Tag.
- Otherwise, if you are a citizen of another tribe in the Farele Free Travel Area, you must have valid government identification from your tribe. These include:
- Leecan Student Licenses
- Leecan Teacher Licenses
- Leecan Non Licenses
- Demaugian Intertribal Tickets
- Xunilean Farele Travel Passes
- Otherwise, if you are a citizen of a tribe in the Enigma Alliance, you must have Travel Document FE1-D issued by your tribe.
- Otherwise, if you are a citizen of a tribe in the Preton Alliance, you must be accompanied by a government official from your tribe.
- Otherwise, if you are from the continent of Barin or any of its islands (see Travel Document 6-B for a list of all islands considered by Ciers to be as part of Barin), please fill out Travel Document 6-C prior to meeting with Border Services.
- Otherwise, please fill out Travel Document 6-D prior to meeting with Border Services.
Unfortunately, due to the current state of intertribal affairs, Ciers will not be accepting Intertribal Passports as a valid form of identification. We apologise for any inconvenience.
Thank you for your cooperation!

View File

@ -3,6 +3,7 @@ title: Wet Hair
date: 2020-06-26 date: 2020-06-26
tags: tags:
- unstagnation - unstagnation
- featured
--- ---
*Splash!* *Splash!*

View File

@ -3,6 +3,7 @@ title: A Favour
date: 2021-06-20 date: 2021-06-20
tags: tags:
- unstagnation - unstagnation
- featured
--- ---
*Ring…ring…ring…* *Ring…ring…ring…*

View File

@ -0,0 +1,117 @@
---
title: "The Birds on Censorship"
date: 2021-07-09
tags:
- birds
- unstagnation
---
**Summary:** Sudden news rocks the Birds' world as they reminisce on their past and their future.
<!-- more -->
---
All is well in the Bird residence. Brandy has come over to hang out today and is kindly preparing breakfast for Skoomer Bird and Noodle Bird, who sit across from each other at the wooden dining table. Their buddy Mango is also in the basement, chilling and playing video games.
Skoomer chuckles as he flips through a particularly interesting page in the newspaper. "Noodle," he says, turning the paper around and leaning forward to show her its contents. "Take a look at these things they banned recently."
Noodle raises her attention from her phone to Skoomer, squinting to read the tiny inked letters. "Wow. Damn."
"And this is just a small section, the rest of the pages are…completely expected." Skoomer sighs. "Some of these I expected too. Algorithms nowadays are incredible at filtering these out, and there is human review as well. I guess to the *common* person this…sounds incredibly scary, doesn't it?" he says, smiling and shaking his head.
"Mm," Noodle agrees. "What happens if you break these rules?" She shifts her gaze back to her phone, brow furrowed. "What if I accidentally posted hentai? Will I go to jail?"
Skoomer's reply is prompt and direct. "Instant deletion and maybe an account ban. Keep in mind, since your account is connected to your ID number, they know who posted it as well. *Exactly* who posted it."
Noodle's face pales for an unknown reason. "Uh oh. Uh…wait, doesn't League count as lewd content? They have 'sexy characters' — like a lot of them basically wearing bikinis." She rapidly types on her phone as she says this.
Skoomer raps his fingers against the table. "League's problem is more of a copyright issue, I would say," he hums. "Bikinis are fine, I think…in practice."
"This is Janna." Noodle raises an eyebrow, holding her phone up to Skoomer, which is showing a picture of a fantastic (d.: "of or relating to fantasy") woman wielding a spear in very revealing attire.
Skoomer nods. "Yeah, this is fine. Nothing wrong at all."
Confusion shows itself on Noodle's face as she slightly lowers her phone. "Oh…okay, never mind — maybe I don't understand what 'lewd' means."
At that moment, Brandy comes in, setting a plate of eggs and bacon in front of both of them. He groans exasperatedly. "Guys, it's too early for this debate."
Skoomer waggles his eyebrows. "Not for me."
Brandy thinks about this for a moment. "True," he decides. "Carry on, then." He steps away back to the kitchen for the other still-sleeping members of the Bird household.
Skoomer puts his newspaper away and now considers Noodle's earlier statement. "Lewd…it's a bit more blurry, I guess. But I think the interesting part is like," he waves away the matter, "the ban on LGBTQ content and underage dating."
"I mean, it *is*, but I'm not surprised about that," Noodle waves away the wave.
"…Because it's reflective of the policies in…many other fields," Skoomer continues.
This piques Noodle's interest. "Oh?"
"At the end of the day, it's not LGBTQ that is being prohibited," Skoomer argues, "but actually the organisations that advocate for it."
Brandy suddenly pops his head back in from the kitchen, frying pan in one hand. "Oh yeah, is underage dating illegal? I remember Skoomer got someone expelled for doing that." He pauses. "Maybe," he adds.
"*Technically*," Skoomer states matter-of-factly, raising a finger in the air, "underage dating is not forbidden as a law, but as local school or district policies."
Brandy shrugs. "Basically a law, then. A by-law, you could say."
Noodle looks disappointed but not terribly surprised. "Skoomer. Why."
Skoomer clasps his hands together and looks straight in Noodle's eyes. "It's my duty." He smiles.
"Oh," Brandy starts, "I remembered that correctly?"
"Just change 'someone' to a plural and yeah."
"Well, of course," Brandy says, amused. "It takes two of them."
"Ah, well, more than two," Skoomer corrects him. "Also, I designed a policy that actually doesn't do it in pairs."
Noodle lets out a gasp of horror and utter shock, eyes wide and hands cupped around her mouth. "Skoomer! You were in a *threesome?"*
Brandy laughs. Skoomer stares at her pointedly. "Ma'am, excuse me *what*? What are you *thinking?*"
Noodle's expression is one of purely innocent confusion.
"Noodle asking the real questions! Ah, Mango, you're up — let me grab some breakfast for you." Brandy turns to head back to cooking, occasionally letting out a chuckle. "A threesome…heh."
The two at the dining table turn to see Mango up from the basement with a blank expression.
"My disappointment is immeasurable and I am questioning my friendships," he states.
At this point, Noodle gives up on restraining herself and laughs whole-heartedly. "Bwa ha ha!"
Skoomer throws his hands up in the air, grumbling indecipherable noises.
Calming herself down, Noodle is still smiling widely as she teases Skoomer further. "I don't know, I just kinda assumed Skoomer got into a relationship to expose the other person — and then he said 'more than two', and I was like…" She squints suspiciously.
Skoomer crosses his arms defensively. "Sir, we didn't play undercover, it was elementary school."
"I don't know about that, Skoomer…"
"Even our counter-espionage didn't go that far!"
"Sus."
"Fine! *One* thing I did was uh…we only expelled one person in a pair, but the pair had to choose who got expelled." He smirks self-assuredly.
"Mate," Mango interjects, staring at Noodle, "'more than two'…are you cheating on me?"
Noodle looks at him sheepishly as she slowly slides down in her chair until only her eyes peek above the table. Mango's expression is one of pure devastation.
Skoomer chuckles. "Nah, but Noodle, wouldn't you agree that my policy is highly effective?" he says proudly. "The *speed* that couples just *disintegrated* amazed me at the time."
"Do you know what else is effective?" Mango directs a side-eye glance at Skoomer.
Skoomer tilts his head away and side-eyes him right back.
Mango smirks.
Skoomer dips his head.
Mango smirks harder.
Skoomer begins to sweat nervously.
To Noodle's dismay, any possible resolution to the tension between them is broken by Runo crashing down the stairs. "Yo guys, did you see the newspaper?" He shoves a finger at a tiny line on the newspaper clutched in his other hand. "Any action that damages ethic unity," he quotes. "WHAT THE FRESH FUCK!"

View File

@ -0,0 +1,135 @@
---
title: "The Birds on Language and Tea"
date: 2021-07-10
tags:
- birds
- unstagnation
---
**Summary:** The Birds discover Asia and its many variants of leaf juice. Waffle Bird is innocent.
<!-- more -->
---
The living room is a mess to Noodle Bird's eyes. Papers are strewn across the rug and hardwood floor with Runo lying face down and limbs splayed. There are no words to describe what must have happened.
"What happened to Runo? Why is he talking about cum?"
Runo's giggles are muffled from the floor. "Cum," he slowly enunciates.
Brandy Bird sighs from his corner, reaching down from his spinny chair to give Runo's head a solid bonk. "Bonk — go to horny jail."
Noodle's gaze flits over to Skoomer Bird, who shifts further into his desk by his open laptop as if to disassociate himself from Runo. "Well, uh…maybe he is high," he offers.
Between fits of laughter, Runo defends himself. "I'm not horny!" he protests. "It's just a *funny* word. Especially since CANAMOO used it in their official solutions…as a short form for 'cumulative'."
Noodle nods in understanding. "Oh…I see. That's the *worst abbreviation ever!"* She lapses into giggles along with Runo who pounds a fist on the rug.
"I know, right? Waffle is just perpetually horny though, I dunno what's up with her."
Brandy snorts. "Brand new phrase right there."
At the same time, from the mention of her name, Waffle pokes her head around the corner from the neighbouring room. "Woah, wait — what. What?" she demands.
Runo hits a button on his phone, waving away the matter. "It's okay, nobody cares. Also, Brandy, I don't think that's new — that's a very old sentence and I've heard it at least, like, seven times."
*"Uwu cumsy wumsy!"* Waffle's tinny but clearly cheerful voice blares through Runo's phone speaker.
"Bruh — now I've been called 'perpetually horny' *and* 'universally *moist*'," Waffle sniffs. "I don't know what is *up* with you people!"
Runo pushes himself into a sitting position, shrugging smoothly. "If it happens multiple times, it's probably you."
"No!" Waffle vehemently denies. "I think they are just *bad* because I am innocent!"
"Nobody innocent would innocently say 'uwu cumsy wumsy'," Runo points out. "You can't *do* that."
Waffle turns away from them and huffs an annoyed huff. "Well, I am gonna blame it on the lack of outside time messing with my brain!"
Skoomer decides to offer his own opinion on the matter. "Consider decreasing your caffeine overdose," he suggests helpfully.
"Come on, it was *one* coffee I bought yesterday," Waffle insists, "and I'm still not done."
"She doesn't like coffee, she said." Runo also reminds Skoomer. "It's boba."
Skoomer squints suspiciously at Waffle, who throws her hands up and gestures wildly at his accusations. "I literally drank half of the coffee and couldn't continue! Also I was really tired! And jittery. And tired!"
Skoomer considers his past experiences with Waffle. "You are perpetually jittery," he notes.
"Goddammit!"
Runo laughs. "I mean, coffee tastes *good* cold."
There is a noise of unadulterated disgust from Brandy's corner. He slowly turns and stares into the depths of Runo's soul with an unsatisfactory expression. "How do you expel the landlord?"
Waffle sticks her tongue out. "Coffee tastes terrible," she generalises. "No amount of sugar can cover how terrible coffee is. And it doesn't even work!"
This grave insult to their dignities goes unnoticed by both Brandy and Runo as they stare each other down. "What?" Runo demands. "Cold coffee is literally a thing you can buy at most places. You're just *uncultured* — not my problem."
"Like Bappachino or something?" Brandy asks.
"Moondollars!" Waffle interjects with the name of a popular coffee chain.
"I don't know coffee," Runo puts out as a disclaimer, palms held outward. "Just that cold coffee is good."
"Bruh," says Brandy. "Well, I guess that's fine — but I thought you meant hot coffee that went cold." The tension slowly dissipates as the misunderstanding is resolved.
"I mean, that's fine as well," Runo amends.
Waffle's eyes sparkle as she gasps when she remembers something amazing. "$1 iced coffee at McBirb's!"
The group only notices at this time that Skoomer has been shaking with visible anger in his seat, his fingers frozen above his keyboard.
"Calm down, green tea enjoyer," Runo faces away from him but turns back soon after. "Okay, I'm sorry — green tea is good as well…sometimes."
Skoomer returns to his work, appeased.
"Green tea good!" Waffle chirps. Noodle returns and cleanly bonks her from the side. "Yes! Black tea also good!"
"Green tea good!" Noodle agrees and leaves again.
"Orange pekoe can die in a hole!"
Runo makes an indescribable face.
"Literally *so bad!"* Waffle continues. "Worse than Earl Grey! And that already *sucks!"*
Skoomer sighs as he sips his own cup of tea. "Xihu Longjing for the win, by the way," he declares smugly.
Runo looks at him blankly. "The fuck is that?"
Skoomer shakes his head at Runo's lack of knowledge of Skoomer customs, heaving out truly the most disappointed of sighs, then launches into what would be a multi-minute explanation of its origin, benefits, and superior flavour and attributes.
He doesn't even reach ten seconds when Runo decides that he'd rather listen to something more interesting. "LMAO, okay."
Waffle flops backward onto the couch, arms out in either direction. "This cup looks so *thick*…" She points at the bubble tea clipart plastered on the ceiling of the room. "I want a thick cup of bubble tea…" Her whisper embodies longing rivalling that of the attraction between the sun and the Earth.
"It's just short," Runo dismisses.
"I want to buy some bubble tea right now…" Tears leak out of the poor girl's eyes as her bubble hopes and dreams lay crushed, malnourished, and emptied.
Skoomer notices this and doubles down. "Short…" he agrees, "like Waffle."
Waffle instantly jumps up, hair flying as she uses the full extent of her height to refute Skoomer. "No! Wrong!"
Runo shakes his head in mock disapproval. "Skoomer," he chides, "you're the only tall one here." He scooches a little closer. "We out*number* you…"
Skoomer trains his eyes on Runo, slowly rolling his spinny chair back an inch for each inch Runo approaches. "Bread!" he calls. "Save me!"
Bread Bird crashes down the stairs and freezes when greeted by the sight of Runo and Waffle staring hungrily at Skoomer. "What the fuck is going on."
Runo ignores the newcomer. "We will take your headphones first!" he declares.
"Take all his headphones!" Waffle joins in, flanking Skoomer's evacuation path. "He can't stop us!"
"He probably has enough for all of us — so rich!"
"N-no," Skoomer nervously denies, flicking his eyes occasionally to Bread in a plea for help.
Bread shakes his head sadly and salutes. "Skoomer, I cannot defend you in this scenario. Your YN-2000YN4 is too good."
"Nooo!"
Granted implicit permission, Waffle cheers and tackles Skoomer clear off of his chair, scrabbling for the headphones around his head.
Bread looks on sadly, but with no regrets.

View File

@ -0,0 +1,143 @@
---
title: "The Birds on Posterior Acoustics"
date: 2021-07-12
tags:
- birds
- unstagnation
---
**Summary:** The Birds are confronted with a dilemma that they must resolve. Curiosity of human flesh knows no bounds.
<!-- more -->
---
'Tis but a normal evening in the Bird household. Waffle Bird once again rants about the injustices of the education system by the dining table as Muffin Bird and Egg listen attentively. At the same time, Brandy Bird and Noodle Bird pretend to listen attentively while playing a racing game together on the TV.
Soup Bird doesn't bother pretending as he scribbles on papers for the upcoming school year.
"I'm so scared of accidentally unmuting in Frances's class," Waffle finishes, breathing hard as she concludes her latest rant. Muffin and Eggy nod along in complete agreement as Runo chuckles at his phone from the couch.
*"I'm so scared of accidentally unmuting in Frances'* ass." The surprise on Runo's face is completely fake as Waffle's mangled words pierce through even the racing game's music and Soup's focus on work.
There is silence except for the slowly quieting *vroom*s from the bots that overtake Brandy's and Noodle's cars.
Muffin is the first to speak up. "Runo," he says. "Kill yourself."
Soup snorts and is unable to hold in laughter until an urgent thought strikes him, a potential misunderstanding that must be resolved in his mind. "But…" he ponders, "how do you unmute…in someone's ass? How does that work?"
Attention shifts from Runo as now everyone focuses on Soup's incomprehensible question.
"Runo…" Brandy says slowly, "you can meme him."
Eggy thinks it over and comes to a conclusion. "How *doesn't* it work?" he asks quizzically but without elaboration.
"It doesn't make logical sense?" Soup's confusion is not shared by the other members in the household. "Like," he tries to explain, "the only way I can think of —"
What the only way Soup can think of will never be known as Eggy interrupts him. "Getting it *in* there might not make sense, but if it was already there, what's stopping you from unmuting?"
An understanding "ah" is followed by a nod from Soup as he follows along. "Ohh. So —"
"What the fuck." Runo's sentiment is completely agreed with by everyone else as Eggy and Soup continue to discuss the merits of an electronic device in the human body.
Soup continues, "— there means that *Waffle* is in *Frances'* ass, and then…she talks?"
As the mental image slowly dawns upon everyone in the room to varying degrees of disgust, Eggy's expression makes it clear he's suddenly changed his mind. "I advise you stop thinking about it."
Noodle clutches her head, shaking it frantically, her controller having dropped to the ground. Her eyes attempt to roll into the back of her head but fail. "Oh my god please delete this from my eyes — i can't — I just imagined it, now it's stuck…"
"I mean…good acoustics?" Waffle's expression is frozen in a gentle smile as she tries to wrap and simultaneously unwrap the idea around her head. "What even…"
"Can I censor Soup for the good of our sanity?" Brandy asks everyone.
"No." Eggy makes it seem like they're having a perfectly normal discussion.
"Yes," Brandy insists.
"What the fuck do you mean by 'acoustics'?" Muffin says accusingly after finally letting the conversation sink in, tempting further discussion.
"It's a small enclosed space," Eggy reminds him in a sensible tone, receiving an incredulous face in return.
"But who would be listening?" Morbid curiosity fills Noodle's voice as she dares to go further. Her question goes unanswered, however, as the others pile on to correct Eggy's acoustic sense.
"As an audiophile, I feel offended." The indignation of Muffin's declaration is unparalleled. He opens his mouth to continue, but —
"But it's like a tube?" Soup's innocent question leaves Muffin's mouth hanging open.
"True," Eggy concedes. "There might be an echo."
"Censor Soup, *right now*," Muffin demands, ripping off a strip of duct tape.
"It's the large intestine, right?" Soup carries on, oblivious to potential death threats behind him.
"Yeah."
The tubular description helps Waffle realise something. "Like, you know how practising your instrument in the washroom makes it, like, sound okay? Decently?"
"But, like," Soup starts, "I don't think sound is very reflective on flesh."
"I dunno," Waffle frowns, thinking it over. "I practised in the bathroom with my friend once. Practised an instrumental, I mean."
"Human tissue is soft and absorbs stuff really easily," Eggy adds helpfully.
The sheer urge to provide concrete facts stalls Muffin long enough to prevent him from performing violent actions on Soup for the time being. "It is in fact not," he confirms, then steps menacingly closer. "I call upon the pope to strike Soup with lightning."
"Please do," Waffle groans, face in palm once she realises she's meaningfully taking part in the discussion. She joins the others in being unable to turn away but desiring to be as far removed as possible.
"Also, there are other substances in the large intestine that absorb the sound," Soup realises.
Muffin glowers at him.
"…So the acoustics aren't good probably," Eggy concludes, nodding to himself.
Unable to help herself, Waffle reluctantly returns to the shining pillar that is Soup slowly reasoning in the moat of implications of their current topic. "Okay, so many not good acoustics."
"Yeah." Soup nods in satisfaction. "URK —" In a bout of desperation to stop him from ruining the others' evening, Muffin takes it upon himself to strangle Soup from behind.
"Soup," he says sadly, "please die in a hole."
"Maybe," Waffle agrees, looking on but not moving. "LOL!"
Using the underside of his chair as leverage, Soup in a moment of clarity forces his head forward and grips Muffin's arms to throw him up and over. Surprised, Muffin's grip tightens and Soup follows along as they spin twice together before collapsing into a heap on the floor.
Inadvertently releasing his neck, Muffin glares angrily but frozen in place as Soup pins him down with his knees and an arm, the other being used to rub his throat. "I don't know… It doesn't make much sense. Okay," he nods at Muffin, "I'm high. I should stop."
Muffin sighs in relief as both the topic of conversation ends and he's released from the Soup's restraints. "Did she ever say that she spoke in his ass?" he wonders.
Muffin's duct tape he dropped is picked up by Brandy. He turns it over in his hands, examining it. "Am I justified in censoring Soup if he keeps talking about this?"
"Yes."
"Soup's EE —" Muffin jokes, brushing himself off. "An acoustic study of sound fields in the ass of Mr. Frances."
"Get Frances to supervise," Brandy adds, unable to resist.
A shiver runs through Soup as he considers this. "…Gonna get *roasted*. Hm, actually, what do you think if I — Mmph!"
Any of Soup's further inquiries are locked in his mind as Brandy finally slaps the duct tape around his mouth to a standing ovation.
------
Extra:
"Shit." Soup Bird buries his head in his hands. "Agggh…"
"What is it?" Noodle Bird leans over to see Soup Bird's screen, then winces as she reads the text. "Ah, you got *him* as your mentor? RIP — What topic are you doing?" She turns her eyes to the subject and topic submitted for approval, and her eyes widen.
"Ah." Noodle bursts out laughing, falling backward into her chair.
Soup groans, rubbing his head in pain and defeat as his eyes rescan the introductory message from his Extended Essay mentor.
*Hi Soup,*
*I took a look at your topic and found it quite interesting. However, you should consider expanding the scope of your experiment to asses in general.*
*Speaking of people in front of your computer, you might want to recruit some others so that you have a wide range of data over multiple repeats. You also would have to provide sufficient controls such as diet and routine, ensuring that the substances in each of your research locations are clearly known, or risk losing all marks for not having a proper methodology in your experiment.*
*I've already approved and locked in your topic. Please let me know of your research question and we can further discuss your research and methodology at our first formal meeting tomorrow.*
*Mr. Frances*

View File

@ -0,0 +1,110 @@
---
title: "The Birds on Waffle Supremacy"
date: 2021-11-01
tags:
- birds
- unstagnation
- featured
---
**Summary:** The Birds enter a debate at school over the supremacy of waffles over pancakes. Obviously, one of them is the better food, but it's up to them to prove it.
<!-- more -->
---
In the student-lined halls of Vaybiew Secondary School, a lone poster hangs on a bulletin board, its top-right corner limply drooping in front of it. Colours faded from hours of abuse, a singular golden arrow labelled "waffle supremacy" directs any offendees to room 233.
Let us head into the room to observe the intellectual debate occurring between the factions.
Room 233 is styled as a traditional court. Dimly lit, intricately designed mahogany furnishes the room, from the chairs to the desks to the court podiums that are illuminated by spotlights. A substantial audience listens to the four people debating, raptured by the engaging arguments flying across the room.
"The only reason Egg hates pancakes," Noodle Bird is declaring, pounding a fist on the podium before her, "is because there are eggs in pancakes. I'm gonna cook your entire family, you twat."
Egg of the waffle camp glares at her.
The spotlight suddenly switches to Bean Bird as she slams the objection buzzer. "There are eggs in waffles too!" A pause. "But waffles are better than pancakes," she adds.
Noodle recoils at this new evidence, staggering back as if she's been shot. She grips her podium tighter.
Emboldened by this turn of events, Bean goes on the offensive. "The cubes are a better vessel for syrup!" she fires. "You're just bland, Noodle."
Noodle's argument is in shambles and she needs a moment to collect her thoughts — a moment taken by the audience to lean more in favour of the waffle camp.
"No! Pancakes are better!" Noodle racks her brain for any argument while she quickly distracts the audience.
Bean smirks knowingly, sensing victory. "Imagine being so wrong."
Noodle sighs. It's time for their secret weapon — a shame to lose it so early on, though. She nods almost imperceptibly to a shadowy figure at the back of the courtroom.
Coloured confetti is shot from both sides behind Noodle while a large banner with the pancake emblem unfurls from the ceiling as she spreads her arms, beaming. "Pancakes!" she announces righteously.
Bean can only stare at the effective tactic that earns applause from the audience. "Noodle," she says sadly, shaking her head disappointedly.
Sensing that the waffle camp is losing control of the narrative, Egg jumps in desperately to provide reinforcement behind Bean. "Pancakes," he says loudly, quieting the applause, "are just slabs of *cooked dough*. Waffles are elegantly shaped for optimum tastiness."
In front of their emblem, the pancake faction regains their momentum. "No," Noodle retorts, "y'all are so cubed. Imagine not being round!"
"Bruh, what the hell." Runo as the moderator makes a disgusted face at the image of a waffle filled with syrup. "Are you supposed to put that much syrup?"
Noodle takes the opportunity, signaling again to her spy in the audience. "Pancakes have *curves!"* Jets of confetti again fly behind her at the last word. The audience cheers as celebratory music plays, and some of them are already holding up their vote for pancakes.
"Waffles also have curves!" Egg interjects, looking to steal the spotlight.
Runo frowns. "Wait, so you think they have sex appeal?" The cheering, music, and atmosphere in the room immediately falls apart, to be filled with awkward silence.
"Where'd you get that from?" Egg finally asks.
"Isn't that what 'curves' means?"
"No," Bean recovers, answering Runo's first question after a delay. "Waffles have more sex appeal because…cubes…you can…"
"Hol' up," someone in the audience says, to universal agreement.
The debate mood is completely stifled until Egg fiddles with the projector enough to beam an image of a glorious syrup-filled waffle topped with butter and cream onto the debate screen. "The beautiful waffle!" he declares, gesturing at the golden dish. The music is restored and the audience cheers — some of them even change their vote.
"It's super sexy — I'm getting hard already!" Waffle Bird pokes her head out from backstage, to general laughter. "Hi! Help!"
"That's so hot, I'm gonna cum," Bean swoons, starry-eyed, her hands clasped in admiration.
Noodle taps the microphone, creating feedback that reverberates around the room, restoring balance. Satisfied that she is the centre of attention, she begins attacking the golden food. "Can squares roll around?" She takes the microphone out of its holder and steps in front of the podium. "No, I don't think so. Circle supremacy!" Her fist pumped in the air at the last line, a whole tub of confetti pours out behind the curtains, which gets the audience up and excited again. The waffle camp frowns.
"Waffles roll better than pancakes do," Egg corrects Noodle. "They have thicker edges." To reinforce his point, he puts a second image on the screen — a stack of perfectly browned pancakes topped with a square of butter dripping with syrup, outlining their limpness and impractical rollability. "Boring pancake."
"Right?" Bean turns to the audience, nodding. The audience nods along.
"What do you mean — the pancake looks beautiful!" Noodle protests.
Waffle takes this time to poke her head out again from backstage, stepping firmly on the side of the waffle camp. She glances at the screen and shivers. "Ew… my pp just shriveled up." Noodle gapes at her as the audience laughs.
"Pancakes will flop over," Egg says. The debate has returned to normal and the two parties are once again at each other's throats in a normal fashion.
"Like my pp when I see one," Waffle adds.
"They double as wagon wheels!" Bean concludes. The pancake camp is once again in shambles. Noodle gestures at the moderator for their last secret weapon.
Runo from his moderator chair, quieting the audience. To the waffle camp's dismay, he strides dramatically to the pancake podium. There is a sharp gasp from someone in the crowd. "I personally think…" pause for dramatic effect, "*pancakes* are better because they have more *fluff*."
This abdication of responsibility has the waffle team on full offensive, their rage and passion for waffles on full display.
"Nah," Bean dismisses.
"But it feels *dry* in your mouth!" Waffle brushes him aside.
"Unless you consume them a millisecond after they're cooked," Bean agrees. "They flop down."
"Wha —" The pancake faction clearly expected a bigger reaction, and Runo scrambles to counter. "Wait, what do you mean, 'flop'? I have never heard of this."
"They compress," Bean says matter-of-factly.
"Wait, really?" It's almost as if Runo is a paid actor for the pancake camp by how surprised he is at this nonsense.
"Because the heat and steam hold them up."
"They *deflate,* yeah!" Waffle interjects.
"Right!" Bean nods along. Right on cue, the screen somehow falls off with the pancake still on it and limply folds into itself like a bathroom towel.
"I've heard enough." The judge at the other end hammers his gavel, and all debate stops. "I hereby declare — that the waffle camp has won this debate! This meeting is now adjourned."
"You're just a grid lover — that's all you'll ever be," Noodle grumbles as she steps off the darkened stage.

View File

@ -4,6 +4,7 @@ date: 2021-08-19
tags: tags:
- ibia - ibia
- unstagnation - unstagnation
- featured
--- ---
"…And never come back again, boy!" "…And never come back again, boy!"

View File

@ -0,0 +1,100 @@
---
title: "A is for Alcohol and IAs"
date: 2022-10-24
tags:
- birds
- unstagnation
- featured
---
**Summary:** Bean Bird and Noodle Bird enjoy a chat together on a quiet night slaving away before they leave in the summer. Bean wishes she had a stronger drink.
<!-- more -->
---
Quiet keys clack and pencils scribble as the Bird family finds itself working, scrambling to tie up loose ends in their final months together. Their upcoming move to Universe City weighs upon all of their minds. There is no room for frivolous discussion, no time for anything but the epitome of productivity.
"Guys, I hate my AIF. With a *passion*." Bean Bird sets down a half-filled mug and she glares all around, slamming her hands on the table. "AIF sucks *ass*. But, like, not good ass."
Noodle Bird blinks, her attention suddenly lifted from her 50k-word real person fic. "Oh."
Bean continues her tirade, invigorated. "Poopy crusty ass! Like, straight white male unwashed ass." Sighing, she lays her head down on her arm for only a second before she sits upright again, strangling thin air with her bare hands. "I'm fucking *losing my mind* *I hate this IA so much."*
"Why?" For the life of her, Noodle can't imagine what difficulties one could possibly have with the Internal Assessment. The soul-wrenching dread of the IA has avoided her completely.
"I never thought I had to listen to a British guy appealing to the crowds to work with the *USSR*." She peeks at the video between her fingers like it's out to doom her and her alone. "NOOOOOO!"
"Oh, god," Noodle says sadly. There is nothing she can do. Bean must walk her own path down this chosen path, or die trying.
"It's okay," Egg assures her, with the confidence of one who has yet to experience true suffering. "Get a level 5 and get it over with."
"I *can't!"* Bean bemoans. "I did so much research! There are *so many things* in my brain!" She grabs the mug and chugs furiously. "Why won't Wroscell let me get drunk and spew out all the data to him?"
"Bro," Noodle says, patting her, "I was literally procrastinating on researching my IA until, like, yesterday! And here I am!" — she gestures at her computer, now filled with lines upon lines of words no one could possibly know was a 55k-word real person fic.
Bean thumbs through a textbook, gazing at page after page of notes. "The sticky notes in my books are so colourful…" she says wistfully, her eyes lighting up, "…and so *toxic!"*
"Eat it! Maybe it'll taste sweet!" Noodle says, eyeing the textbook stuffed with solid, packed handwriting and the tantalising aroma of adhesive.
Bean leans in beside Noodle. "I'm gonna eat you," she whispers.
"I…don't think I'm very delicious." A chair subtly scooches away.
Returning to her mug, Bean cradles it once again and drinks deeply, leaning back against her chair. She sighs. "Rose-flavoured tea is disgusting."
"I agree."
"I have to pretend I like it because my mother bought it for me. And it's like, three dollars." Bean makes a face as she sips at her cup, staring longingly into it as if hoping it were something else.
Sensing the family crisis to be just about over, Egg chimes in, "Human meat tastes like chicken." He looks as if he's been dying to join the discussion the whole time.
"Wrong," Bean scoffs. "It tastes like pork."
Egg is firmly removed from the conversation from sheer embarrassment. "Yeah, that," he says, slinking away, never to be seen again.
A thought occurs to Bean — she taps her chin thoughtfully. "Human flesh is commonly called 'long pig'. And calamari is often pig anus."
"What."
"Yuh," Bean nods, "I have really weird taste in podcasts."
Noodle seriously considers the topic. "We *are* technically longer than pigs…most of us."
"Nood you're not," Bean frowns. "You're tiny!" She notices the glare sent her way and her expression softens pityingly. "I love you, Noodle, but you're *teeny tiny*."
Bean's sincerity moves Noodle's heart and she deflates. "I know… I love you too."
"I would drink all the rose tea if it meant you didn't have to…" Bean swoons, occasionally stopping to sip out of her mug. "It's okay, you'll grow."
Noodle looks up. She knows.
Evidently, Soup also wanted to jump in earlier but was dissuaded by Egg's shutdown. Now he takes his opportunity. "Any…cannibals in the chat?"
Soup is utterly ignored as Bean continues her musings with Noodle. "I think a lack of gravity in heaven elongates the joints." She laughs. "See what I did there?"
Soup laughs along.
Bean shakes her head. "I swear to god, the day before IAs are due, I'm just delirious." As if to illustrate her point, her face plants into her keyboard.
"…IA is due tomorrow?" Soup ventures.
Bean turns her head to face him, eyes bleary and hair falling in front of her face. "Do you have any idea how many times I stayed up until four in the morning to finish my chem IA?" She pushes herself up. "And it wasn't even the day before the IA was due either! It was like, three days in advance!" She grabs another mug and chugs the whole thing down. "I just stressed myself out and dragged Snac with me to explain hydrogen bonding sites in hydrolysed sugar — that's when I learned that you can physically break bonds with hands."
To demonstrate, Bean mimes a karate chop and smashes the wooden table. Various sheets of paper fly into the air while laptops fly to the ground.
Noodle stares. "Huh. That's cool."
Soup wordlessly picks up his cracked laptop and leaves to cry in a corner.
Bean doesn't notice. "I feel like half the things I say here today can be in an archive." She shrugs. "I dunno, I feel delirious."
Dusting off her screen, Noodle places her laptop on her lap and continues to work away at her 60k word real person fic."Nothing like learning good ol' hydrogen bonding to have a great bonding experience at 4 am."
Bean nods, swaying in her chair. "I got drunk once off peach soju and this was the same experience, except I didn't have a *history IA* to write," she murmurs.
"It's the stress plus adrenaline! But it's fun!"
Bean's eyes droop. "So. Much. Fun." With her last words, Bean Bird falls out of her chair and crashes on the ground, never to be woken again…
…until the next morning, when she realises that her IA is due tomorrow and she still has to write two body paragraphs and a conclusion.

View File

@ -0,0 +1,97 @@
---
title: "High Crimes and Misdemeanours"
date: 2022-10-28
tags:
- birds
- unstagnation
---
**Summary:** An egg catches the Birds in a dastardly scheme. They must take their revenge by public humiliation. There will be no escape.
<!-- more -->
---
An egg stands up on the stage by the corner of the road, adjusting his tie. He's ready to campaign. It'll be the biggest advance in politics in the history of politics. It'll be the crowning achievement of his whole career. His magnum opus.
By the end of the day, he *will* have passed his referendum.
"If you think poutine is great, sign here!"
The masses turn as one. For one time-stopping moment, the egg knows he has their attention, and he intends to use it. They see nothing but an innocent man in a tie, gesturing at a sheet of paper on the board. Several neon arrows flash to make it extra clear.
Indeed, it does say, *Poutine appreciation board!*
Noodle Bird immediately brandishes her pen and signs the sheet.
"Yes! Poutine is so *good!"* Soup wastes no time, pushing through the crowd to have his voice heard.
"*Yes!*" Bread Bird practically throws himself in front of the paper to put his name down.
Other passersby stop to sign their support for the rich food. The egg smiles. All according to plan.
"Fantastic." Once he reaches seven signatures, it's over. He studies the list of names, smirking. They don't know.
Slowly, in front of the star-struck poutine fans, the egg peels off the tape on his sheet to reveal the actual title of the signature sheet:
*IB appreciation board!*
Instantly, the crowd roars in outrage. The sight of their dedication to the best food ever being turned to an advertisement for some snooty high school program drives them into a frenzy.
"BAN HIM!"
"THIS TREACHERY!"
The egg takes a step back, wiping sweat off of his forehead. Perhaps he should have secured himself before unveiling his scam political maneuver.
"Yo yo yo *what!* I got *scammed!"* Soup's anger is palpable and it rouses the crowd to further raise their pitchforks. "I can prove my innocence though!" He waves a picture of the paper from before the egg turned traitor. "I said poutine!"
Faced with unmistakably the truth, people begin to settle down, knowing that this egg will be made a fool of in the news. Unfortunately for him, he has yet to leave the frying pan yet. "But this egg," Soup turns to face him, eyes blazing. "He needs to stand on *trial."*
"We must stand him on trial!" Bread cheers.
"For high crimes!"
"Yes!" Soup roars. There is nothing but passion now, passion and hatred for the egg who dared to bait their beloved food.
"I wanna be the judge, jury, *and* executioner!" Noodle shouts.
"This is unprecedented," Soup declares, spreading his arms. Somehow, he's made it to the stage and has found a soapbox to stand on.
"We must make a witch trial for his crimes!" Bread joins him on the stage. "I propose we open a court!" Someone lugs a judge's desk in front of him as he and Soup put on their judicial gowns.
Noodle raises her hand. "I propose scrambled eggs!"
The egg pales.
"I second!" says Brandy.
"Yes," Soup nods, considering the motion. "That's good…but isn't boiling him better?"
The egg wrinkles his nose. "Ew. Boiled egg."
"Scramble him with ketchup…" Bread muses, stroking his fake white beard. Almost immediately, the enthusiasm becomes mixed.
"What the fuck?" Noodle questions Bread's age. "Who puts ketchup on scrambled eggs?"
Bread tries to get the trial back on track, but somehow persistent, no-wrong-answer controversies must take precedent.
The egg sees an opportunity. He squints knowingly, waiting for his moment to strike.
"Yes!" Brandy waves his arms harder. "Scrambled eggs and ketchup are good!"
"And pepper!" the egg helpfully adds. "IKEA breakfast." The egg's carefully crafted statement suddenly turns the people's attention away. Bread glares at him, about to strike the gavel for order, but to his shock, the small hammer suddenly disappears. The chattering among the people grows.
"Oh, okay." Noodle nods. "Should we add salt?"
Much to his horror, Bread finds himself thinking about the concept. "IKEA meatballs?" he gasps, realising the egg's plan. He shakes his head rapidly to rid himself of the tangent. Seeing the audience's anger begin to decline, Bread comes to a decision. "We hold another trial!" he declares, trying to speak over everyone else.
It's no use. The unified front has broken down, killed by infighting and squabbling over meaningless, irrelevant topics. Already his judge's chair and gown have been returned to their original owners.
The egg's injustice will go unpunished. He confidently strides away, knowing that there is no one who can stop him now. He holds power now that he's no longer the centre of attention.
Bread desperately reaches out, trying to rip the signature sheet out of the egg's hand, but someone from the crowd pulls him back. As he's questioned about drugs and integration, he looks helplessly to the egg who ruined them all.
Bread clenches his fist. He will not forget this. One day, he will have his revenge. His heart has hardened. From this day onward, no longer will he sign any such "poutine appreciation board". No longer will he trust others so unconditionally. The egg has done something that he will never forget, destroyed something inside Bread that will never return.
A single tear falls from the side of his cheek.

View File

@ -0,0 +1,117 @@
---
title: "Soup's Egirl Discord Status"
date: 2022-10-25
tags:
- birds
- unstagnation
---
**Summary:** Mango reveals a hidden secret of Soup's past. The Birds listen in and offer their own opinions. Runo wants more.
<!-- more -->
---
Soup sits in the Bird residence living room, once again playing League. All is well in the world.
Mango taps his shoulder behind him. "Did you find our Clash team yet?"
"Monkeys," Runo sniffs haughtily at the game as he passes by.
Mango pulls out a seat beside Soup. "Ask your e-girls," he insists. "You should ask."
"*Fiiiine.* But I don't think they know you too well…"
"What's her name?" Mango taps his chin, racking his brain to remember the list. "Christina?"
"Most e-girls are too great." *For you* stays unsaid. Runo comes along again and pauses.
"Yeah, I don't care." Mango waves away the matter. "You should ask the Bronze I one, to lower our MMR." He spreads his hand, wiggling his eyebrows. "Genius?"
Runo is frozen at this newest revelation to his life.
"Okay," Soup chuckles, "let me go ask."
Finally over his shock, Runo manages to force his mouth open enough to express his disbelief. "There are *multiple?*"
"Uh…" Soup's eyes dart around shiftily.
"Oh," Mango snorts, "multiple is an *understatement*."
"Uh oh," Runo says, squinting at Soup.
All eyes are on him now. There is no opportunity to escape. All of the exits are conveniently blocked off, and already more people are entering the living room with their own work. Did they hear? Soup briefly considers the amount of space available underground to store several bodies.
In the end, he decides to take the politician approach: deflect, gaslight, and get outraged. "WHAT?"
"Like, you count them in dozens! Wrong unit, mate." Heedless of the inner turmoil going on inside Soup's head, Mango shakes his head as he continues to sabotage his friend.
Runo squints harder.
"Zero dozens!" Soup tries to shut it down, but it's too late now.
"A platoon!" Muffin suggests.
Mango's eyes light up. "A concentration camp! Hmm…"
Muffin eyes him knowingly. "HMMM…"
Soup makes a general noise of disagreement.
Noodle Bird joins the group. "I have a folder just for his e-girls," she taunts, waving a manila envelope.
"What the fuck?" Hungrily eying the envelope, Runo dashes after Noodle who bolts away, cackling as she clutches it to her chest. "I'm kidding! The folder is just for his girl friends!"
"WHAT?" Soup tries outrage again, sure it will work this time.
Evidently not.
"Yeah! Like, he has like, at least 20 —" Before Mango can go on, Soup decides that one casualty is acceptable for the cost and begins to strangle him. "— 15! 15 egirls." Soup releases his neck. "At least."Mango dodges around Soup's attempt to slice fruit, smirking. "And it's increasing every day! Poggers!"
"Exponentially?" Muffin leans in.
"No sir. *Factorial."* Mango winks.
"Oh, *shit*."
Runo returns only to hear that and immediately prostrate himself at Soup's feet, inadvertently restraining him from commiting a crime.
"True!" Mango joins him, the two kneeling and bowing to their hero.
Slightly flustered by the sudden change in attitude, Soup tones down his murder tendencies and drops his knife. "Um…what? This is false. I know, like, *negative* girls."
"That's the most bullshit statement I have *ever* heard." There's no keeping secrets when Mango's around.
"I thought you had a girlfriend," Runo frowns.
It pains Soup to admit it, but it's the only way to counteract the slander. "I'm…single right now."
Rain starts to pour outside like the pathetic fallacy it is. The fanatic light in Runo's eyes fades away. "Wait, really? Um…oof?"
"False!" Mango declares as the sun pokes through the clouds. "I'm *literally* your boyfriend!" He spreads his arms, beaming like the rainbow forming outside.
"True!" Soup returns the embrace.
Runo turns away, uninterested. "F," he says flatly. "Wait..."
Muffin joins this celebration of LGBT rights by playing a kazoo.
"No, he's not!" Runo points an accusing finger at the duo. "I didn't get the joke! Fuck!"
Mango sighs theatrically, caressing Soup's face with his hand, gazing longingly into his eyes. He's drowning in the wave of beauty of those pearlescent orbs. A sea of feeling, of affection, of love, all of it wells up inside him. An **emocean.**
"If you're gonna break up with me," he says sadly, "at least do it in private."
"Wait, he *is!*" Assuaged by the display of true love, Runo falls to his knees to repent. Mango bows.
Soup steps back and laughs. "Anyways, Mango, I don't really have many 'e-girls' to ask. I asked Christina, but I'm pretty sure her answer is going to be no."
"Ask *what*?" Runo interjects.
"I'm confused too." The humble Brandy wears a befuddled expression as he stands in the doorway, freshly arriving into the conversation of e-girls.
"I don't," Soup brushes the matter away. "Mango asked."
And that was the end of that conversation. Mango never brought up the topic again, Runo moved on to complaining about politics, and Soup never had to share his list of e-girl friends.
All was well.

View File

@ -0,0 +1,196 @@
---
title: "Miraidon the Dongo"
date: 2024-11-14
tags:
- birds
- unstagnation
- featured
---
![Dongo is love. Dongo is life.](/dongo-party.png)
<!-- more -->
The boy falls back against his couch, dropping his controller in his lap. The video game on his TV blares cheerful adventurous music. "The game is sooooo slow."
"Fuck," the girl says over video chat. "You're on emulator, though. 2.5x it?"
"The computer isn't quite powerful enough."
"L. Ratio. Cringe PC gamer."
The boy turns his attention back to the game, adding a new monster to his current party. "Dongo disagrees. Do you have a Dongo? Exactly."
On the other side of the world, the girl has little to say to that. "Dongo isn't _real,_ he's _not\!"_
"Dongo is life."
"It's time to snap back to reality — car doggo is a myth."
"I will write Dongo fanfic. He will be real."
She makes a face. "I'm so sorry — this sounds like the biggest innuendo…"
He nods absently. "Hm, hm, yes, sex the Dongo."
She pauses. _"What?"_
He frowns. "Absolutely not. Dongo is too precious."
Her jaw drops. "You're the one who said to sex it\! Every time I settle down to play Pokémon, I end up playing League instead. I think I've been corrupted by the impure." She shudders.
"Oh, no\! League is not fun."
"But it is," she says sadly.
"It's boring\! You click things, it's barely real time nor turn-based. It's as if tower defense and Civ did a Dongo."
She takes a moment to parse the mystery word. "Why is it an _adjective_ now?"
"Dongo disapproves." He shakes his head.
"Oh, yeah? Well, I've spoken to Dongo and he said he doesn't like you." She sticks her tongue out, folds her arms, and turns away.
"Lies\!" he cries. "You don't have Dongo. Dongo is _mine\!"_
"I have him right here," she gloats, peeking off-camera. "He's right in front of me right now. He just doesn't want to see you."
"Let's see 'em."
"But he doesn't want to see you."
"Pics or it didn't happen."
Some shuffling from her background. "Every time I pull out my camera, he hides behind the couch and mutters, 'no'. And he can talk. And I'd know because I've talked to him."
"I think you met an impostor Dongo…" he starts, concerned.
"I think _you_ met an impostor Dongo," she counters.
"…because Pokémon can't talk, duh?" He sounds befuddled.
"Not to you," she says smugly. She pauses. "I feel like such a kid…ahh…"
"Embrace it," he encourages. "Embrace the Dongo."
"…I'd like to remind you of the innuendo you set up not 40 seconds ago."
"Well, it was an…accident."
"Dongo disapproves."
"No, _your_ Dongo disapproves."
"It's not worth arguing with a Dongo flat earther, so I'll agree to disagree."
"I think you should get your head checked out because Dongo isn't real? He's from this Pokémon game."
_"What?"_ she screeches. "No — heathen — _hypocrite\!_ I am _awake_, I am _alive_, I am _correct\!"_
"Dongo disagrees."
"Dongo is _right here,_ I —" Some shuffling. She sounds defeated. "I can't win." One last stand. "If he's real, then I'm right and you're wrong. But if he's fake you're right but you're right because you heard from Dongo."
"Touch the Dongo," he eggs. A simple test. "I dare you."
"…What the _fuck_ is going on? I'm not _coherent_ enough for this?" She sounds like she's in hysterics.
"More like you're _too_ coherent for this. I'm making innuendo jokes, obviously. Can't you tell? Dong get it?"
_"AHHHHHHHHHHH\!"_ The sound of a head smashing a wall comes through the speaker.
"No. No, I refuse to accept this. Dongo is — one: real, two: not real, three: an innuendo, four: I need a psych eval, five: _you_ need a psych eval."
He frowns. "Three of those criteria contradict each other."
"…I am _not prepared_ for this multiple choice test."
"You see, if I need an eval, then Dongo must not be not real nor real — and simultaneously you don't need an eval because Dongo is therefore real and not real. This is according to Dongo over here."
Silence.
"Dongo says hi…" he adds.
A pause. "I am going to lose my mind."
"…in Dongonese."
"What the _fuck_ is _Dongonese?_ You said Pokémon can't talk\!"
"They can — what do you mean? Pikachu talks all the time."
A slow realisation. "I'm being fucking gaslit\!"
"Can't you hear them? They make all sorts of sounds in the game."
She falls over. She considers whether to go batshit crazy or lie on the ground. She decides to lie on the ground.
"They even talk to each other," he continues. "Here, lemme find a pic."
A ping. With dread in her heart, she slides down the notification.
![](/dongo-gaslight.jpg)
It's a picture of a Victorian lamp. Its flame cheerfully dances, burning the gas. "Shut the fuck up."
"Stare into it," he encourages.
Against her will, her eyes track the still image. "Oh," she realises.
"You can hear whispers."
"_Oh._ I can _see,"_ she whispers.
" 'Dongo dong dong dongo.' "
"I can _hear_ it. The voices. Of _Dongo_. They're telling me…they're telling me to go see a therapist." She pushes herself up with her elbows.
"Dongo agrees."
"_No\!"_ She is fed up. She will not let him take it back from her. "Let him speak for _himself."_
" 'Cause that's a gas light, y'know. It's not good for your brain. Has fumes and all that."
She looks back to the picture with horror in her eyes. "I feel like I'm forgetting how to communicate like a functional human being," she says.
"It's okay\! Dongo feels that way too."
She closes her eyes. "Maybe I'll wake up tomorrow and this will all have been a big, elaborate dream."
"Yep."
"_He's. Not. Real."_
"That's right. He can't hurt you."
Something in that resonates with her. "H-he can't hurt me," she says, dazed. Then more confidently, "He can't hurt me." She nods. "The voices are gone."
"Good."
She takes a deep breath. "I am about to have a psychotic break on the bathroom floor at 1:47 AM over hearing the voices of Miraidon, box legendary of hit new Pokémon game Violet, and maybe this other nerd's voice, I'm not sure."
"While you're at it, say hi to Dongo for me."
"I'll tell him you hate him."
This shakes him up. His tone changes. "Noooo\!" More confidence. She pulls the middle finger to no one in particular. But then, "That's okay. I have the real Dongo."
She refuses to be sucked into his insanity. "I'm _not_ starting this again, I'm not\!"
He sighs. It must have been a long night for him, too. "I'd agree, actually."
"Ha\! I can finally win\!"
"I've run out of circles to Dongo," he concedes.
The sense of victory makes it easier for her to assert her place in reality. "That doesn't even make sense\!"
"So? Does Dongo make sense? No."
"The message I'm taking away from this conversation is that I'm always right and you're always wrong and Schrodinger's Dongo."
"Sounds about right." A pause. "Good night\!"
"Night\!"
!["Don-dongo. Dongodon dong dongodon?"](dongo.jpg)

View File

@ -1,29 +0,0 @@
# HOI HOI
```js
export default defineNuxtConfig({
content: {
highlight: {
// Theme used in all color schemes.
theme: 'github-light'
// OR
theme: {
// Default theme (same as single string)
default: 'github-light',
// Theme used if `html.dark`
dark: 'github-dark',
// Theme used if `html.sepia`
sepia: 'monokai'
}
}
}
})
```
for some reason **it no** work
hey wtf
- list test
- list test
- list test

View File

@ -7,7 +7,9 @@ export type Language =
| "react" | "react"
| "markdown" | "markdown"
| "flutter" | "flutter"
| "android"; | "android"
| "rust"
| "golang";
export interface Project { export interface Project {
name: string; name: string;
href: string; href: string;
@ -16,6 +18,7 @@ export interface Project {
longDescription?: string; longDescription?: string;
langs: Language[]; langs: Language[];
license?: "AGPL-3.0" | "GPL-3.0" | "MIT" | "LGPL-3.0"; license?: "AGPL-3.0" | "GPL-3.0" | "MIT" | "LGPL-3.0";
type: "web" | "tool" | "embedded" | "service";
} }
export const projects: Project[] = [ export const projects: Project[] = [
@ -26,8 +29,9 @@ export const projects: Project[] = [
"A comic downloader and converter to CBZ / EPUB / PDF for my Kobo.", "A comic downloader and converter to CBZ / EPUB / PDF for my Kobo.",
longDescription: "Available via CLI and a Qt GUI!", longDescription: "Available via CLI and a Qt GUI!",
langs: ["python"], langs: ["python"],
license: "LGPL-3.0", license: "AGPL-3.0",
img: "mandown.webp", img: "mandown.webp",
type: "tool",
}, },
{ {
name: "Noveldown", name: "Noveldown",
@ -35,8 +39,9 @@ export const projects: Project[] = [
langs: ["python"], langs: ["python"],
license: "LGPL-3.0", license: "LGPL-3.0",
description: description:
"A webnovel downloader and converter to EPUB for my Kobo, with lots of metadata!", "A webnovel downloader and EPUB converter for my Kobo, with lots of metadata!",
longDescription: "Heavily borrows Mandown's design.", longDescription: "Heavily borrows Mandown's design.",
type: "tool",
}, },
{ {
name: "Jeopardy", name: "Jeopardy",
@ -46,6 +51,7 @@ export const projects: Project[] = [
license: "AGPL-3.0", license: "AGPL-3.0",
description: "Kahoot-inspired Jeopardy! game, including Final Jeopardy!", description: "Kahoot-inspired Jeopardy! game, including Final Jeopardy!",
longDescription: "Created for Bayview's Computer Club.", longDescription: "Created for Bayview's Computer Club.",
type: "web",
}, },
{ {
name: "Primoprod", name: "Primoprod",
@ -56,15 +62,17 @@ export const projects: Project[] = [
description: description:
"A game simulator to increase productivity with quests and gambling.", "A game simulator to increase productivity with quests and gambling.",
longDescription: "My first project with a JS framework!", longDescription: "My first project with a JS framework!",
type: "web",
}, },
{ {
name: "Eifueo", name: "PillowⓇ",
href: "https://github.com/potatoeggy/eifueo", href: "https://github.com/potatoeggy/ece198",
langs: ["markdown"], description:
"A water quality statistics aggregator written for the STM32 microcontroller with a display and keypad.",
langs: ["rust"],
license: "GPL-3.0", license: "GPL-3.0",
img: "eifueo.webp", type: "embedded",
description: "A collection of rewritten notes to remember things better.", img: "pillow.webp",
longDescription: "THIS IS NOT A TEXTBOOK.",
}, },
{ {
name: "Napbot", name: "Napbot",
@ -72,36 +80,9 @@ export const projects: Project[] = [
langs: ["python"], langs: ["python"],
license: "AGPL-3.0", license: "AGPL-3.0",
description: description:
"A Discord bot initially to track sleep hours as friendly competition but is now a local music bot with synchronised lyrics!", "A Discord music bot with synchronised lyrics, originally a sleep tracking bot to encourage sleeping.",
img: "napbot.webp", img: "napbot.webp",
}, type: "service",
{
name: "Resketch",
href: "https://github.com/anyuan-chen/resketch",
langs: ["typescript", "react"],
img: "resketch.webp",
description:
'A "reverse-Pictionary" where you compete to have an AI recognise your drawings.',
longDescription: "Written for YRHacks 2022.",
},
{
name: "Perdiem",
href: "https://github.com/anyuan-chen/perdiem",
langs: ["javascript", "react"],
license: "AGPL-3.0",
img: "perdiem.webp",
description:
"A pretty budget tracking app where I learned too much about server-side rendering.",
longDescription: "Written for StormHacks 2022.",
},
{
name: "RecipeReady",
href: "https://github.com/christopherlam888/recipe-ready-frontend",
langs: ["python", "android", "flutter"],
img: "recipeready.webp",
description:
"Android app to automagically plan meals and prepare a shopping list so you don't have to.",
longDescription: "Written for Hack the North 2021.",
}, },
{ {
name: "AutoFicFare", name: "AutoFicFare",
@ -110,15 +91,38 @@ export const projects: Project[] = [
license: "GPL-3.0", license: "GPL-3.0",
description: description:
"Automatically update fanfiction in a Calibre database to instantly update them on your Kobo.", "Automatically update fanfiction in a Calibre database to instantly update them on your Kobo.",
type: "tool",
},
];
const unreleasedProjects: Project[] = [
{
name: "Aleister",
href: "https://github.com/potatoeggy/aleister",
langs: ["rust"],
license: "AGPL-3.0",
type: "service",
}, },
{ {
name: "Website", name: "Aoto",
href: "https://github.com/potatoeggy/public", href: "https://github.com/potatoeggy/aoto",
description: langs: ["golang", "typescript", "react"],
"This website! It's gone through three iterations before this one, and this one's the first to use a framework.",
langs: ["typescript", "vue"],
license: "AGPL-3.0", license: "AGPL-3.0",
img: "public.webp", type: "web",
},
{
name: "Kobink",
href: "https://github.com/potatoeggy/kobink",
langs: ["rust"],
license: "AGPL-3.0",
type: "service",
},
{
name: "GBARR",
href: "https://github.com/potatoeggy/gbarr",
langs: ["rust"],
license: "GPL-3.0",
type: "embedded",
}, },
]; ];

13
data/siteRevisions.ts Normal file
View File

@ -0,0 +1,13 @@
interface SiteRevision {
title: string;
url: string;
}
export const revisions: SiteRevision[] = [
{
title: "Nuxt 3 (2022)",
url: "https://eggworld.me",
},
{ title: "Eleventy (2021)", url: "https://2021.eggworld.me" },
{ title: "Vanilla (2019-2020)", url: "https://2020.eggworld.me" },
];

3
data/specialTags.ts Normal file
View File

@ -0,0 +1,3 @@
export const SpecialTags: string[] = [
"featured",
];

View File

@ -4,12 +4,20 @@ export interface TagData {
} }
export const tagInfo: Record<string, TagData> = { export const tagInfo: Record<string, TagData> = {
barin: { name: "Barin" }, barin: {
name: "Barin",
description:
"Welcome to Barin — a world in constant conflict between productivity and procrastination.",
},
bsscc: { bsscc: {
name: "BSSCC", name: "BSSCC",
description: "Posts related to Bayview's Computer Club.", description: "Posts related to Bayview's Computer Club.",
}, },
ibia: { name: "Ibia" }, ibia: {
name: "Ibia",
description:
"A Kurious child struggles to fight the misinformation brought by the Six Goddesses of the Subjects.",
},
misc: { name: "Miscellaneous" }, misc: { name: "Miscellaneous" },
poetry: { poetry: {
name: "Poetry", name: "Poetry",
@ -27,5 +35,36 @@ export const tagInfo: Record<string, TagData> = {
description: description:
"A collection of very short stories written to do something productive during JuneAugust 2020 and August 2021.", "A collection of very short stories written to do something productive during JuneAugust 2020 and August 2021.",
}, },
albatross: {
name: "The FOSS Albatross",
description:
'Articles about free and open source software. Also available on <a href="https://medium.com/the-foss-albatross">Medium</a>.',
},
birds: {
name: "Bird Family",
description:
"A large, loving family of birds who have found in each other a kindred soul for eternal suffering.",
},
birdseye: {
name: "Bird's-Eye View",
description: "What's the world like to a pair of human-watching bluebirds?",
},
uoft: {
name: "University of Teyvat",
description: "A <em>Genshin Impact</em> university AU.",
},
nanowrimo: {
name: "NaNoWriMo",
description:
"Story snippets written during National Novel Writing Month as part of a larger work.",
},
skyprojections: {
name: "Projections in the Sky",
description: "Dreams or reality — what is the difference?",
},
featured: {
name: "Featured",
description: "Works that are less rambly and more actually good!",
},
}; };
export default tagInfo; export default tagInfo;

View File

@ -1,5 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
useHead({ title: "Eggworld" }); import { revisions } from "@/data/siteRevisions";
useHead({ title: "Oeufs?" });
</script> </script>
<template> <template>
@ -7,19 +8,38 @@ useHead({ title: "Eggworld" });
<Navbar /> <Navbar />
<slot /> <slot />
<footer <footer
class="flex flex-col items-center p-3 bg-gray-100 w-full text-sm dark:bg-gray-800" class="flex items-center justify-between p-3 bg-gray-100 w-full text-sm dark:bg-gray-800 flex-col md:flex-row gap-2"
> >
<p> 2022 Daniel Chen</p> <label class="flex items-center gap-2">
<p>Revision:</p>
<!--
the onchange is so bad - i'd rather it be done through vue
but nuxt is genuinely screwing me over here
ig r4 has to be in next.js
-->
<select
class="p-2 border rounded-lg dark:bg-[#222]"
onchange="location = this.value"
>
<option v-for="(r, i) in revisions" :key="i" :value="r.url">
{{ r.title }}
</option>
</select>
</label>
<div class="flex flex-col items-center">
<p> <p>
Licensed under the AGPL-3.0 on Licensed under the AGPL-3.0 on
<a class="underline" href="https://github.com/potatoeggy/public"> <a class="underline" href="https://github.com/potatoeggy/public">
GitHub</a GitHub</a
> >
and and
<a class="underline" href="https://git.eggworld.tk/eggy/public"> <a class="underline" href="https://git.eggworld.me/eggy/public">
Gitea Gitea
</a> </a>
</p> </p>
</div>
<div class="w-36"></div>
</footer> </footer>
</div> </div>
<slot name="top-button" /> <slot name="top-button" />
@ -29,7 +49,9 @@ useHead({ title: "Eggworld" });
html { html {
background: white; background: white;
color: black; color: black;
transition: color 0.2s ease, background 0.2s ease; transition:
color 0.2s ease,
background 0.2s ease;
overflow-x: hidden; overflow-x: hidden;
overflow-y: scroll; overflow-y: scroll;
scroll-behavior: smooth; scroll-behavior: smooth;
@ -51,6 +73,13 @@ html::before {
z-index: 0; z-index: 0;
} }
/* div#__nuxt {
min-height: 100vh;
}
it's better if everything is sort of long but that is not the case
*/
html.dark::before { html.dark::before {
transform: translateX(0); transform: translateX(0);
} }

View File

@ -1 +0,0 @@
<template>error!</template>

View File

@ -1,13 +1,41 @@
import { defineNuxtConfig } from "nuxt"; import { defineNuxtConfig } from "nuxt/config";
import svgLoader from "vite-svg-loader"; import svgLoader from "vite-svg-loader";
// https://v3.nuxtjs.org/api/configuration/nuxt.config // https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({ export default defineNuxtConfig({
compatibilityDate: "2024-10-16",
app: {
head: {
htmlAttrs: {
lang: "en",
},
meta: [
{ name: "viewport", content: " width=device-width,initial-scale=1" },
{ name: "theme-color", content: "#ffffff" },
],
link: [
{ rel: "icon", type: "image/x-icon", href: "/favicon.ico" },
{
rel: "stylesheet",
href: "https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css",
},
],
script: [
{
defer: true,
src: "/script.js",
hid: "stupidEmergencyScript",
type: "module",
},
],
},
},
modules: [ modules: [
"@nuxt/content", "@nuxt/content",
"@nuxtjs/tailwindcss", "@nuxtjs/tailwindcss",
"@nuxtjs/color-mode", "@nuxtjs/color-mode",
"@nuxtjs/sitemap", "@nuxtjs/sitemap",
], ],
css: ["@/assets/css/main.scss"],
nitro: { nitro: {
prerender: { prerender: {
routes: ["/sitemap.xml"], routes: ["/sitemap.xml"],
@ -16,8 +44,11 @@ export default defineNuxtConfig({
typescript: { typescript: {
shim: false, shim: false,
}, },
site: {
url: process.env.BASE_URL || "https://eggworld.me",
},
sitemap: { sitemap: {
hostname: process.env.BASE_URL || "https://eggworld.tk", strictNuxtContentPaths: true,
}, },
tailwindcss: {}, tailwindcss: {},
colorMode: { colorMode: {
@ -25,18 +56,13 @@ export default defineNuxtConfig({
}, },
vite: { vite: {
plugins: [svgLoader()], plugins: [svgLoader()],
css: {
preprocessorOptions: {
scss: {
api: "modern",
},
}, },
head: {
meta: [
{ name: "viewport", content: " width=device-width,initial-scale=1" },
],
link: [
{ rel: "icon", type: "image/x-icon", href: "/favicon.ico" },
{
rel: "stylesheet",
href: "https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css",
}, },
],
}, },
content: { content: {
documentDriven: false, documentDriven: false,
@ -79,8 +105,9 @@ export default defineNuxtConfig({
}, },
}, },
experimental: { experimental: {
reactivityTransform: true, sharedPrerenderData: true,
},
features: {
noScripts: true,
}, },
target: "static",
ssr: true,
}); });

View File

@ -7,21 +7,20 @@
"preview": "nuxt preview" "preview": "nuxt preview"
}, },
"devDependencies": { "devDependencies": {
"@nuxt/content": "npm:@nuxt/content-edge@latest", "@nuxt/content": "^2.13.4",
"@nuxtjs/color-mode": "^3.1.4", "@nuxtjs/color-mode": "^3.5.1",
"@nuxtjs/sitemap": "^2.4.0", "@nuxtjs/sitemap": "^6.1.2",
"@nuxtjs/tailwindcss": "^5.3.0", "@nuxtjs/tailwindcss": "^6.12.1",
"@tailwindcss/typography": "^0.5.2", "@tailwindcss/typography": "^0.5.15",
"nuxt": "npm:nuxt3@latest", "@types/node": "^22.7.5",
"dayjs": "^1.11.13",
"nuxt": "3.13.2",
"prettier": "^3.3.3",
"reading-time": "^2.0.0-1", "reading-time": "^2.0.0-1",
"rehype-katex": "^6.0.2", "rehype-katex": "^7.0.1",
"remark-math": "^5.1.1", "remark-math": "^6.0.0",
"sitemap": "^7.1.1", "sass": "^1.79.5",
"typescript": "^4.7.4", "typescript": "^5.6.3",
"unist-util-visit": "^4.1.0", "vite-svg-loader": "^5.1.0"
"vite-svg-loader": "^3.4.0"
},
"dependencies": {
"dayjs": "^1.11.4"
} }
} }

13
pages/404.vue Normal file
View File

@ -0,0 +1,13 @@
<script setup lang="ts">
useTitle("404 - Not Found", "You're lost!");
</script>
<template>
<main class="prose dark:prose-invert max-w-3xl transition">
<h1 class="mb-1">404 - Not Found</h1>
<p>
You're lost! Don't worry, here's a link
<a href="/">back to the home page.</a>
</p>
</main>
</template>

View File

@ -1,16 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import type { BlogParsedContent, StoryParsedContent } from "@/shared/types"; import type { AnyParsedContent } from "@/shared/types";
import { calcReadingTime, getPrettyDate } from "@/shared/metadata"; import { calcReadingTime } from "@/shared/metadata";
type GeneralParsedContent = BlogParsedContent | StoryParsedContent;
const route = useRoute(); const route = useRoute();
definePageMeta({ // definePageMeta({
layout: "withtop", // layout: "withtop",
}); // });
// we're not using ContentDoc because i need control // we're not using ContentDoc because i need control
const doc = await queryContent<GeneralParsedContent>(route.path).findOne(); const doc = await queryContent<AnyParsedContent>(route.path).findOne();
const type = route.path.startsWith("/stories") const type = route.path.startsWith("/stories")
? "stories" ? "stories"
: route.path.startsWith("/blog") : route.path.startsWith("/blog")
@ -21,27 +19,26 @@ const descText =
type === "stories" type === "stories"
? `${calcReadingTime(doc).words.total} words` ? `${calcReadingTime(doc).words.total} words`
: `${calcReadingTime(doc).minutes} min read`; : `${calcReadingTime(doc).minutes} min read`;
useTitle(doc.title); useTitle(doc.title, doc.description);
const captionText = const captionText =
type === "stories" ? "Story" : type === "blog" ? "Blog post" : ""; type === "stories" ? "Story" : type === "blog" ? "Blog post" : "";
</script> </script>
<template> <template>
<div class="container prose dark:prose-invert w-full"> <main class="container prose dark:prose-invert w-full">
<p class="m-0 uppercase font-mono text-sm" v-if="captionText !== ''"> <p class="m-0 uppercase font-mono text-sm" v-if="captionText">
{{ captionText }} {{ captionText }}
</p> </p>
<h1 class="m-0">{{ doc.title }}</h1> <h1 class="m-0">{{ doc.title }}</h1>
<p class="my-2">{{ getPrettyDate(doc) }} · {{ descText }}</p> <p class="my-2"><Date :doc="doc" /> · {{ descText }}</p>
<div class="flex flex-wrap"> <div class="flex flex-wrap">
<Tag <Tag
v-for="(tag, index) in doc.tags" v-for="(tag, index) in doc.tags"
:dest="`/tags/${type}/${tag}`" :dest="`/tags/${type}/${tag}`"
:key="index" :key="index"
> :name="tag"
{{ tag }} />
</Tag>
</div> </div>
<ContentRenderer :value="doc" tag="article" class="pt-0 w-full"> <ContentRenderer :value="doc" tag="article" class="pt-0 w-full">
<template #empty> <template #empty>
@ -49,18 +46,32 @@ const captionText =
</template> </template>
<template #not-found> <template #not-found>
<h1>404 - Not Found</h1> <h1>404 - Not Found</h1>
<p>
Thanks for dropping by! But the page you're looking for can't be
found.
</p>
</template> </template>
</ContentRenderer> </ContentRenderer>
</div> </main>
</template> </template>
<style scoped> <style scoped>
.container { .container {
width: 80%; width: 80%;
max-width: 72ch; max-width: 80ch;
padding-top: 2rem; padding-top: 2rem;
} }
@media screen and (max-width: 600px) {
.container {
width: 90%;
}
.container h1 {
overflow-wrap: break-word;
}
}
* { * {
transition: color 0.2s ease; transition: color 0.2s ease;
} }

View File

@ -1,14 +1,21 @@
<script setup lang="ts"> <script setup lang="ts">
import type { BlogParsedContent } from "@/shared/types"; import type { BlogParsedContent } from "@/shared/types";
useTitle("Blog"); useTitle("Blog", "Ramblings and ideas");
definePageMeta({ layout: "withtop" }); //definePageMeta({ layout: "withtop" });
// TODO: paginate stories // TODO: paginate stories
const docs = await queryContent<BlogParsedContent>("/blog") const docs = await queryContent<BlogParsedContent>("/blog")
.sort({ date: -1 }) .sort({ date: -1 })
.where({ _draft: false }) .where({ _draft: false })
.find(); .find();
const tags = new Set(
docs
.map((p) => p.tags)
.flat()
.sort()
);
</script> </script>
<template> <template>
@ -16,10 +23,19 @@ const docs = await queryContent<BlogParsedContent>("/blog")
class="flex flex-col grow prose dark:prose-invert max-w-3xl gap-6 transition" class="flex flex-col grow prose dark:prose-invert max-w-3xl gap-6 transition"
> >
<h1 class="mb-0">Blog</h1> <h1 class="mb-0">Blog</h1>
<div class="m-0">
Filter:
<Tag
:dest="`/tags/blog/${tag}`"
v-for="(tag, index) in tags"
:key="index"
:name="tag"
/>
</div>
<PostPreviewCard <PostPreviewCard
v-for="(post, index) in docs" v-for="(post, index) in docs"
:key="index" :key="index"
:post="post" :post
type="blog" type="blog"
/> />
</main> </main>

View File

@ -2,21 +2,31 @@
import Services from "@/components/index/services.vue"; import Services from "@/components/index/services.vue";
import About from "@/components/index/about.vue"; import About from "@/components/index/about.vue";
definePageMeta({ layout: "withtop" }); //definePageMeta({ layout: "withtop" });
useTitle("Home"); useTitle("Home", "Personal website!");
const welcomeStrings = ["Welcome!", "Bienvenue!", "欢迎!"];
</script> </script>
<template> <template>
<main class="flex flex-col items-center justify-around gap-8"> <main class="flex flex-col items-center justify-around gap-8">
<h1>Welcome!</h1> <div class="flex flex-col items-center">
<HeaderLoop class="text-bitter font-bold" :strings="welcomeStrings" />
<p>What are you here to see?</p> <p>What are you here to see?</p>
<p>
For my portfolio, please visit
<a class="underline" href="https://github.com/potatoeggy">GitHub</a>
instead.
</p>
</div>
<div <div
class="flex justify-around items-stretch w-full flex-wrap gap-x-8 gap-y-10" class="flex justify-around items-start w-full flex-wrap gap-x-8 gap-y-10"
> >
<BlogStatBox /> <BlogStatBox />
<StoryStatBox /> <StoryStatBox />
<CommitStatBox /> <CommitStatBox />
</div> </div>
<Services /> <Services />
<About /> <About />
</main> </main>

View File

@ -1,14 +1,21 @@
<script setup lang="ts"> <script setup lang="ts">
import type { StoryParsedContent } from "@/shared/types"; import type { StoryParsedContent } from "@/shared/types";
useTitle("Stories"); useTitle("Stories", "Fantasies and worlds");
definePageMeta({ layout: "withtop" }); //definePageMeta({ layout: "withtop" });
// TODO: paginate stories // TODO: paginate stories
const docs = await queryContent<StoryParsedContent>("/stories") const docs = await queryContent<StoryParsedContent>("/stories")
.sort({ date: -1 }) .sort({ date: -1 })
.where({ _draft: false }) .where({ _draft: false })
.find(); .find();
const tags = new Set(
docs
.map((p) => p.tags)
.flat()
.sort()
);
</script> </script>
<template> <template>
@ -16,6 +23,15 @@ const docs = await queryContent<StoryParsedContent>("/stories")
class="flex flex-col grow prose dark:prose-invert max-w-3xl gap-6 transition" class="flex flex-col grow prose dark:prose-invert max-w-3xl gap-6 transition"
> >
<h1 class="mb-0">Stories</h1> <h1 class="mb-0">Stories</h1>
<div class="m-0">
Filter:
<Tag
:dest="`/tags/stories/${tag}`"
v-for="(tag, index) in tags"
:key="index"
:name="tag"
/>
</div>
<PostPreviewCard <PostPreviewCard
v-for="(story, index) in docs" v-for="(story, index) in docs"
:key="index" :key="index"

View File

@ -3,7 +3,7 @@ import { tagInfo, type TagData } from "@/data/tagInfo";
import type { BlogParsedContent } from "@/shared/types"; import type { BlogParsedContent } from "@/shared/types";
const route = useRoute(); const route = useRoute();
definePageMeta({ layout: "withtop" }); //definePageMeta({ layout: "withtop" });
const tag = const tag =
typeof route.params.tag === "string" ? route.params.tag : route.params.tag[0]; typeof route.params.tag === "string" ? route.params.tag : route.params.tag[0];
@ -14,6 +14,9 @@ const docs = await queryContent<BlogParsedContent>("/blog")
.sort({ date: -1 }) .sort({ date: -1 })
.where({ _draft: false, tags: { $contains: tag } }) .where({ _draft: false, tags: { $contains: tag } })
.find(); .find();
const title = details.name ?? `"${tag}"`;
useTitle(title + " Posts", details.description);
</script> </script>
<template> <template>
@ -21,7 +24,7 @@ const docs = await queryContent<BlogParsedContent>("/blog")
class="prose dark:prose-invert max-w-3xl flex flex-col grow gap-6 transition" class="prose dark:prose-invert max-w-3xl flex flex-col grow gap-6 transition"
> >
<div> <div>
<h1 class="mb-0">{{ details.name ?? `"${tag}"` }} Posts</h1> <h1 class="mb-0">{{ title }} Posts</h1>
<p <p
v-if="details.description" v-if="details.description"
v-html="details.description" v-html="details.description"
@ -31,7 +34,7 @@ const docs = await queryContent<BlogParsedContent>("/blog")
<PostPreviewCard <PostPreviewCard
v-for="(post, index) in docs" v-for="(post, index) in docs"
:key="index" :key="index"
:post="post" :post
:highlighttags="[tag]" :highlighttags="[tag]"
type="blog" type="blog"
/> />

View File

@ -3,7 +3,7 @@ import { tagInfo, type TagData } from "@/data/tagInfo";
import type { StoryParsedContent } from "@/shared/types"; import type { StoryParsedContent } from "@/shared/types";
const route = useRoute(); const route = useRoute();
definePageMeta({ layout: "withtop" }); //definePageMeta({ layout: "withtop" });
const tag = const tag =
typeof route.params.tag === "string" ? route.params.tag : route.params.tag[0]; typeof route.params.tag === "string" ? route.params.tag : route.params.tag[0];
@ -14,6 +14,9 @@ const docs = await queryContent<StoryParsedContent>("/stories")
.sort({ date: -1 }) .sort({ date: -1 })
.where({ _draft: false, tags: { $contains: tag } }) .where({ _draft: false, tags: { $contains: tag } })
.find(); .find();
const title = details.name ?? `"${tag}"`;
useTitle(title + " Stories", details.description);
</script> </script>
<template> <template>
@ -21,7 +24,7 @@ const docs = await queryContent<StoryParsedContent>("/stories")
class="prose dark:prose-invert max-w-3xl flex flex-col grow gap-6 transition" class="prose dark:prose-invert max-w-3xl flex flex-col grow gap-6 transition"
> >
<div> <div>
<h1 class="mb-0">{{ details.name ?? `"${tag}"` }} Stories</h1> <h1 class="mb-0">{{ title }} Stories</h1>
<p <p
v-if="details.description" v-if="details.description"
v-html="details.description" v-html="details.description"

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32.000001">
<g transform="translate(0 -1020.3622)">
<ellipse cx="-907.35657" cy="479.90009" fill="#384e54" color="#000" overflow="visible" rx="3.5793996" ry="3.8207953" style="isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1" transform="scale(-1 1) rotate(-60.548)"/>
<ellipse cx="-891.57654" cy="507.8461" fill="#384e54" color="#000" overflow="visible" rx="3.5793996" ry="3.8207953" style="isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1" transform="rotate(-60.548)"/>
<path fill="#384e54" d="M16.091693 1021.3642c-1.105749.01-2.210341.049-3.31609.09C6.8422558 1021.6738 2 1026.3942 2 1032.3622v20h28v-20c0-5.9683-4.667345-10.4912-10.59023-10.908-1.10575-.078-2.212328-.099-3.318077-.09z" color="#000" overflow="visible" style="isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1"/>
<path fill="#76e1fe" d="M4.6078867 1025.0462c.459564.2595 1.818262 1.2013 1.980983 1.648.183401.5035.159385 1.0657-.114614 1.551-.346627.6138-1.005341.9487-1.696421.9365-.339886-.01-1.720283-.6372-2.042561-.8192-.97754-.5519-1.350795-1.7418-.833686-2.6576.517109-.9158 1.728749-1.2107 2.706299-.6587z" color="#000" overflow="visible" style="isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1"/>
<rect width="3.0866659" height="3.5313663" x="14.406213" y="1035.6842" fill-opacity=".32850246" color="#000" overflow="visible" ry=".62426329" style="isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1"/>
<path fill="#76e1fe" d="M16 1023.3622c-9 0-12 3.7153-12 9v20h24c-.04889-7.3562 0-18 0-20 0-5.2848-3-9-12-9z" color="#000" overflow="visible" style="isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1"/>
<path fill="#76e1fe" d="M27.074073 1025.0462c-.45957.2595-1.818257 1.2013-1.980979 1.648-.183401.5035-.159384 1.0657.114614 1.551.346627.6138 1.005335.9487 1.696415.9365.33988-.01 1.72029-.6372 2.04256-.8192.97754-.5519 1.35079-1.7418.83369-2.6576-.51711-.9158-1.72876-1.2107-2.7063-.6587z" color="#000" overflow="visible" style="isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1"/>
<circle cx="21.175734" cy="1030.3542" r="4.6537542" fill="#fff" color="#000" overflow="visible" style="isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1"/>
<circle cx="10.339486" cy="1030.3542" r="4.8316345" fill="#fff" color="#000" overflow="visible" style="isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1"/>
<rect width="3.6673687" height="4.1063409" x="14.115863" y="1035.9174" fill-opacity=".32941176" color="#000" overflow="visible" ry=".72590536" style="isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1"/>
<rect width="3.6673687" height="4.1063409" x="14.115863" y="1035.2253" fill="#fffcfb" color="#000" overflow="visible" ry=".72590536" style="isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1"/>
<path fill-opacity=".32941176" d="M19.999735 1036.5289c0 .838-.871228 1.2682-2.144766 1.1659-.02366 0-.04795-.6004-.254147-.5832-.503669.042-1.095902-.02-1.685964-.02-.612939 0-1.206342.1826-1.68549.017-.110233-.038-.178298.5838-.261532.5816-1.243685-.033-2.078803-.3383-2.078803-1.1618 0-1.2118 1.815635-2.1941 4.055351-2.1941 2.239704 0 4.055351.9823 4.055351 2.1941z" color="#000" overflow="visible" style="isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1"/>
<path fill="#c38c74" d="M19.977414 1035.7004c0 .5685-.433659.8554-1.138091 1.0001-.291933.06-.630371.096-1.003719.1166-.56405.032-1.207782.031-1.89122.031-.672834 0-1.307182 0-1.864904-.029-.306268-.017-.589429-.043-.843164-.084-.813833-.1318-1.324962-.417-1.324962-1.0344 0-1.1601 1.805642-2.1006 4.03303-2.1006 2.227377 0 4.03303.9405 4.03303 2.1006z" color="#000" overflow="visible" style="isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1"/>
<ellipse cx="15.944382" cy="1033.8501" fill="#23201f" color="#000" overflow="visible" rx="2.0801733" ry="1.343747" style="isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1"/>
<circle cx="12.414201" cy="1030.3542" r="1.9630634" fill="#171311" color="#000" overflow="visible" style="isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1"/>
<circle cx="23.110121" cy="1030.3542" r="1.9630634" fill="#171311" color="#000" overflow="visible" style="isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1"/>
<path fill="none" stroke="#384e54" stroke-linecap="round" stroke-width=".39730874" d="M5.0055377 1027.2727c-1.170435-1.0835-2.026973-.7721-2.044172-.7463"/>
<path fill="none" stroke="#384e54" stroke-linecap="round" stroke-width=".39730874" d="M4.3852457 1026.9152c-1.158557.036-1.346704.6303-1.33881.6523m23.5840973-.3951c1.17043-1.0835 2.02697-.7721 2.04417-.7463"/>
<path fill="none" stroke="#384e54" stroke-linecap="round" stroke-width=".39730874" d="M27.321773 1026.673c1.15856.036 1.3467.6302 1.3388.6522"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -0,0 +1,57 @@
<svg version="1.1" height="106" width="106" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="logo" transform="translate(53, 53)">
<path id="r" transform="translate(0.5, 0.5)" stroke="black" stroke-width="1" stroke-linejoin="round" d=" M -9,-15 H 4 C 12,-15 12,-7 4,-7 H -9 Z M -40,22 H 0 V 11 H -9 V 3 H 1 C 12,3 6,22 15,22 H 40 V 3 H 34 V 5 C 34,13 25,12 24,7 C 23,2 19,-2 18,-2 C 33,-10 24,-26 12,-26 H -35 V -15 H -25 V 11 H -40 Z"/>
<g id="gear" mask="url(#holes)">
<circle r="43" fill="none" stroke="black" stroke-width="9"/>
<g id="cogs">
<polygon id="cog" stroke="black" stroke-width="3" stroke-linejoin="round" points="46,3 51,0 46,-3"/>
<use xlink:href="#cog" transform="rotate(11.25)"/>
<use xlink:href="#cog" transform="rotate(22.50)"/>
<use xlink:href="#cog" transform="rotate(33.75)"/>
<use xlink:href="#cog" transform="rotate(45.00)"/>
<use xlink:href="#cog" transform="rotate(56.25)"/>
<use xlink:href="#cog" transform="rotate(67.50)"/>
<use xlink:href="#cog" transform="rotate(78.75)"/>
<use xlink:href="#cog" transform="rotate(90.00)"/>
<use xlink:href="#cog" transform="rotate(101.25)"/>
<use xlink:href="#cog" transform="rotate(112.50)"/>
<use xlink:href="#cog" transform="rotate(123.75)"/>
<use xlink:href="#cog" transform="rotate(135.00)"/>
<use xlink:href="#cog" transform="rotate(146.25)"/>
<use xlink:href="#cog" transform="rotate(157.50)"/>
<use xlink:href="#cog" transform="rotate(168.75)"/>
<use xlink:href="#cog" transform="rotate(180.00)"/>
<use xlink:href="#cog" transform="rotate(191.25)"/>
<use xlink:href="#cog" transform="rotate(202.50)"/>
<use xlink:href="#cog" transform="rotate(213.75)"/>
<use xlink:href="#cog" transform="rotate(225.00)"/>
<use xlink:href="#cog" transform="rotate(236.25)"/>
<use xlink:href="#cog" transform="rotate(247.50)"/>
<use xlink:href="#cog" transform="rotate(258.75)"/>
<use xlink:href="#cog" transform="rotate(270.00)"/>
<use xlink:href="#cog" transform="rotate(281.25)"/>
<use xlink:href="#cog" transform="rotate(292.50)"/>
<use xlink:href="#cog" transform="rotate(303.75)"/>
<use xlink:href="#cog" transform="rotate(315.00)"/>
<use xlink:href="#cog" transform="rotate(326.25)"/>
<use xlink:href="#cog" transform="rotate(337.50)"/>
<use xlink:href="#cog" transform="rotate(348.75)"/>
</g>
<g id="mounts">
<polygon id="mount" stroke="black" stroke-width="6" stroke-linejoin="round" points="-7,-42 0,-35 7,-42"/>
<use xlink:href="#mount" transform="rotate(72)"/>
<use xlink:href="#mount" transform="rotate(144)"/>
<use xlink:href="#mount" transform="rotate(216)"/>
<use xlink:href="#mount" transform="rotate(288)"/>
</g>
</g>
<mask id="holes">
<rect x="-60" y="-60" width="120" height="120" fill="white"/>
<circle id="hole" cy="-40" r="3"/>
<use xlink:href="#hole" transform="rotate(72)"/>
<use xlink:href="#hole" transform="rotate(144)"/>
<use xlink:href="#hole" transform="rotate(216)"/>
<use xlink:href="#hole" transform="rotate(288)"/>
</mask>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Some files were not shown because too many files have changed in this diff Show More