From f28c56ddd35ab468527eda2b116f300732938205 Mon Sep 17 00:00:00 2001 From: soaos Date: Fri, 3 Apr 2026 17:20:59 -0400 Subject: Initial Commit (Fresh Start) --- .gitignore | 1 + .vscode/settings.json | 8 + CNAME | 1 + README.md | 3 + config.toml | 23 + content/_index.md | 4 + content/about.md | 18 + content/blog/_index.md | 6 + content/blog/blog.css | 99 ++ content/blog/rockbox_stats/index.md | 547 ++++++++ content/blog/rockbox_stats/log-setting.bmp | Bin 0 -> 153666 bytes content/blog/rockbox_stats/playback-settings.bmp | Bin 0 -> 153666 bytes content/blog/rockbox_stats/player.bmp | Bin 0 -> 153666 bytes content/blog/terminal_renderer_mkii/cover.png | Bin 0 -> 6038269 bytes content/blog/terminal_renderer_mkii/david.png | Bin 0 -> 27218 bytes content/blog/terminal_renderer_mkii/davidbayer.png | Bin 0 -> 4213 bytes .../blog/terminal_renderer_mkii/davidthreshold.png | Bin 0 -> 3324 bytes content/blog/terminal_renderer_mkii/index.md | 167 +++ content/blog/vscode_buttplug/buttplug-0.0.1.vsix | Bin 0 -> 632364 bytes content/blog/vscode_buttplug/index.md | 63 + content/heaven/_index.md | 4 + content/heaven/angel.gif | Bin 0 -> 6866 bytes content/heaven/angel2.gif | Bin 0 -> 89484 bytes content/heaven/angel3.gif | Bin 0 -> 8610 bytes content/heaven/bg.jpg | Bin 0 -> 53321 bytes content/heaven/everytime_we_touch_nightcore.ogg | Bin 0 -> 1946392 bytes content/heaven/heaven.css | 24 + content/hell/Flying_Skeleton_Hell.gif | Bin 0 -> 2004165 bytes content/hell/_index.md | 70 ++ content/hell/bg.jpg | Bin 0 -> 48691 bytes content/hell/bigguy.gif | Bin 0 -> 11228 bytes content/hell/comunismo.gif | Bin 0 -> 105570 bytes content/hell/demon.gif | Bin 0 -> 8734 bytes content/hell/demon2.gif | Bin 0 -> 37169 bytes content/hell/demon3.gif | Bin 0 -> 27765 bytes content/hell/demon4.gif | Bin 0 -> 29208 bytes content/hell/demon_face.gif | Bin 0 -> 22848 bytes content/hell/evilmind.gif | Bin 0 -> 101192 bytes content/hell/evilorb.gif | Bin 0 -> 6287 bytes content/hell/firebreak.gif | Bin 0 -> 40755 bytes content/hell/gay.gif | Bin 0 -> 41784 bytes content/hell/gay2.gif | Bin 0 -> 5601 bytes content/hell/gaydudes.gif | Bin 0 -> 101953 bytes content/hell/hell.ogg | Bin 0 -> 924991 bytes content/hell/hellisreal.gif | Bin 0 -> 59555 bytes content/hell/hitler.gif | Bin 0 -> 46349 bytes content/hell/hitler2.gif | Bin 0 -> 45803 bytes content/hell/hitler3.gif | Bin 0 -> 33592 bytes content/hell/hot.gif | Bin 0 -> 4071 bytes content/hell/kissing.jpg | Bin 0 -> 14011 bytes content/hell/obama.gif | Bin 0 -> 1532992 bytes content/hell/pitchfork.gif | Bin 0 -> 16727 bytes content/hell/redfire.gif | Bin 0 -> 21997 bytes content/hell/skull.gif | Bin 0 -> 13021 bytes content/hell/smallfire.gif | Bin 0 -> 5698 bytes content/hell/torch.gif | Bin 0 -> 7960 bytes content/projects/bevy_plugins/index.html | 5 + content/projects/games/NIX_AVREA/index.html | 26 + content/projects/piss_daemon/index.html | 43 + content/projects/piss_daemon/statusbar.png | Bin 0 -> 4546 bytes content/rockstats/index.html | 1301 ++++++++++++++++++++ data/albums.json | 82 ++ static/98.css | 1040 ++++++++++++++++ static/assets/UnifontExMono.woff2 | Bin 0 -> 2002128 bytes static/assets/badges/cc-zero.png | Bin 0 -> 976 bytes static/assets/badges/cookies.png | Bin 0 -> 515 bytes static/assets/badges/go2hell.gif | Bin 0 -> 1577 bytes static/assets/badges/indieweb.png | Bin 0 -> 524 bytes static/assets/badges/javascript.png | Bin 0 -> 412 bytes static/assets/badges/lynx.gif | Bin 0 -> 3303 bytes static/assets/badges/midi_files_now.gif | Bin 0 -> 1395 bytes static/assets/badges/powered-by-debian.gif | Bin 0 -> 904 bytes static/assets/badges/rss.gif | Bin 0 -> 567 bytes static/assets/badges/soaos.png | Bin 0 -> 2563 bytes static/assets/bg.jpg | Bin 0 -> 31580 bytes static/assets/construction.gif | Bin 0 -> 9933 bytes static/assets/icon/button-down-active.svg | 5 + static/assets/icon/button-down.svg | 8 + static/assets/icon/button-left.svg | 8 + static/assets/icon/button-right.svg | 8 + static/assets/icon/button-up.svg | 8 + static/assets/icon/checkmark-disabled.svg | 3 + static/assets/icon/checkmark.svg | 3 + static/assets/icon/close.svg | 3 + static/assets/icon/groupbox-border.svg | 4 + static/assets/icon/help.svg | 8 + static/assets/icon/indicator-horizontal.svg | 6 + .../assets/icon/indicator-rectangle-horizontal.svg | 6 + static/assets/icon/maximize-disabled.svg | 4 + static/assets/icon/maximize.svg | 3 + static/assets/icon/minimize.svg | 3 + static/assets/icon/radio-border-disabled.svg | 7 + static/assets/icon/radio-border.svg | 8 + static/assets/icon/radio-dot-disabled.svg | 3 + static/assets/icon/radio-dot.svg | 3 + static/assets/icon/restore.svg | 10 + static/assets/icon/scrollbar-background.svg | 4 + static/assets/icon/sunken-panel-border.svg | 11 + static/assets/music/867.png | Bin 0 -> 88542 bytes static/assets/music/act_ii.jpg | Bin 0 -> 33123 bytes static/assets/music/apollo.jpg | Bin 0 -> 5380 bytes static/assets/music/atebts.jpg | Bin 0 -> 12938 bytes static/assets/music/batb.jpg | Bin 0 -> 24990 bytes static/assets/music/cwts.jpg | Bin 0 -> 38132 bytes static/assets/music/departure_songs.jpg | Bin 0 -> 49939 bytes static/assets/music/jiminy.jpg | Bin 0 -> 135977 bytes static/assets/music/lysf.jpg | Bin 0 -> 86650 bytes static/assets/music/twin_fantasy.jpg | Bin 0 -> 20076 bytes static/assets/music/wake.jpg | Bin 0 -> 46414 bytes static/assets/music/wetdream.png | Bin 0 -> 72338 bytes static/assets/wrttyh.flac | Bin 0 -> 19614253 bytes static/favicon.png | Bin 0 -> 4287 bytes static/hell.css | 102 ++ static/index.css | 27 + static/index.js | 0 static/style.css | 225 ++++ static/style.css.bak | 224 ++++ templates/about.html | 10 + templates/base.html | 115 ++ templates/blog.html | 37 + templates/heaven.html | 29 + templates/hell.html | 22 + templates/index.html | 60 + templates/macros.html | 62 + templates/page.html | 2 + templates/post.html | 30 + templates/section.html | 2 + templates/shortcodes/age.html | 1 + templates/shortcodes/album_list.html | 16 + templates/shortcodes/soaosed.html | 3 + templates/shortcodes/subtree.html | 12 + templates/shortcodes/title_bar.html | 8 + templates/shortcodes/tree_view.html | 5 + templates/shortcodes/treelink.html | 8 + templates/shortcodes/window.html | 3 + templates/shortcodes/window_body.html | 1 + 136 files changed, 4665 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100644 CNAME create mode 100644 README.md create mode 100644 config.toml create mode 100644 content/_index.md create mode 100644 content/about.md create mode 100644 content/blog/_index.md create mode 100644 content/blog/blog.css create mode 100644 content/blog/rockbox_stats/index.md create mode 100644 content/blog/rockbox_stats/log-setting.bmp create mode 100644 content/blog/rockbox_stats/playback-settings.bmp create mode 100644 content/blog/rockbox_stats/player.bmp create mode 100644 content/blog/terminal_renderer_mkii/cover.png create mode 100644 content/blog/terminal_renderer_mkii/david.png create mode 100644 content/blog/terminal_renderer_mkii/davidbayer.png create mode 100644 content/blog/terminal_renderer_mkii/davidthreshold.png create mode 100644 content/blog/terminal_renderer_mkii/index.md create mode 100644 content/blog/vscode_buttplug/buttplug-0.0.1.vsix create mode 100644 content/blog/vscode_buttplug/index.md create mode 100644 content/heaven/_index.md create mode 100644 content/heaven/angel.gif create mode 100644 content/heaven/angel2.gif create mode 100644 content/heaven/angel3.gif create mode 100644 content/heaven/bg.jpg create mode 100644 content/heaven/everytime_we_touch_nightcore.ogg create mode 100644 content/heaven/heaven.css create mode 100644 content/hell/Flying_Skeleton_Hell.gif create mode 100644 content/hell/_index.md create mode 100644 content/hell/bg.jpg create mode 100644 content/hell/bigguy.gif create mode 100644 content/hell/comunismo.gif create mode 100644 content/hell/demon.gif create mode 100644 content/hell/demon2.gif create mode 100644 content/hell/demon3.gif create mode 100644 content/hell/demon4.gif create mode 100644 content/hell/demon_face.gif create mode 100644 content/hell/evilmind.gif create mode 100644 content/hell/evilorb.gif create mode 100644 content/hell/firebreak.gif create mode 100644 content/hell/gay.gif create mode 100644 content/hell/gay2.gif create mode 100644 content/hell/gaydudes.gif create mode 100644 content/hell/hell.ogg create mode 100644 content/hell/hellisreal.gif create mode 100644 content/hell/hitler.gif create mode 100644 content/hell/hitler2.gif create mode 100644 content/hell/hitler3.gif create mode 100644 content/hell/hot.gif create mode 100644 content/hell/kissing.jpg create mode 100644 content/hell/obama.gif create mode 100644 content/hell/pitchfork.gif create mode 100644 content/hell/redfire.gif create mode 100644 content/hell/skull.gif create mode 100644 content/hell/smallfire.gif create mode 100644 content/hell/torch.gif create mode 100644 content/projects/bevy_plugins/index.html create mode 100644 content/projects/games/NIX_AVREA/index.html create mode 100644 content/projects/piss_daemon/index.html create mode 100644 content/projects/piss_daemon/statusbar.png create mode 100644 content/rockstats/index.html create mode 100644 data/albums.json create mode 100644 static/98.css create mode 100644 static/assets/UnifontExMono.woff2 create mode 100644 static/assets/badges/cc-zero.png create mode 100644 static/assets/badges/cookies.png create mode 100644 static/assets/badges/go2hell.gif create mode 100644 static/assets/badges/indieweb.png create mode 100644 static/assets/badges/javascript.png create mode 100644 static/assets/badges/lynx.gif create mode 100644 static/assets/badges/midi_files_now.gif create mode 100644 static/assets/badges/powered-by-debian.gif create mode 100644 static/assets/badges/rss.gif create mode 100644 static/assets/badges/soaos.png create mode 100644 static/assets/bg.jpg create mode 100644 static/assets/construction.gif create mode 100644 static/assets/icon/button-down-active.svg create mode 100644 static/assets/icon/button-down.svg create mode 100644 static/assets/icon/button-left.svg create mode 100644 static/assets/icon/button-right.svg create mode 100644 static/assets/icon/button-up.svg create mode 100644 static/assets/icon/checkmark-disabled.svg create mode 100644 static/assets/icon/checkmark.svg create mode 100644 static/assets/icon/close.svg create mode 100644 static/assets/icon/groupbox-border.svg create mode 100644 static/assets/icon/help.svg create mode 100644 static/assets/icon/indicator-horizontal.svg create mode 100644 static/assets/icon/indicator-rectangle-horizontal.svg create mode 100644 static/assets/icon/maximize-disabled.svg create mode 100644 static/assets/icon/maximize.svg create mode 100644 static/assets/icon/minimize.svg create mode 100644 static/assets/icon/radio-border-disabled.svg create mode 100644 static/assets/icon/radio-border.svg create mode 100644 static/assets/icon/radio-dot-disabled.svg create mode 100644 static/assets/icon/radio-dot.svg create mode 100644 static/assets/icon/restore.svg create mode 100644 static/assets/icon/scrollbar-background.svg create mode 100644 static/assets/icon/sunken-panel-border.svg create mode 100644 static/assets/music/867.png create mode 100644 static/assets/music/act_ii.jpg create mode 100644 static/assets/music/apollo.jpg create mode 100644 static/assets/music/atebts.jpg create mode 100644 static/assets/music/batb.jpg create mode 100644 static/assets/music/cwts.jpg create mode 100644 static/assets/music/departure_songs.jpg create mode 100644 static/assets/music/jiminy.jpg create mode 100644 static/assets/music/lysf.jpg create mode 100644 static/assets/music/twin_fantasy.jpg create mode 100644 static/assets/music/wake.jpg create mode 100644 static/assets/music/wetdream.png create mode 100644 static/assets/wrttyh.flac create mode 100644 static/favicon.png create mode 100644 static/hell.css create mode 100644 static/index.css create mode 100644 static/index.js create mode 100644 static/style.css create mode 100644 static/style.css.bak create mode 100644 templates/about.html create mode 100644 templates/base.html create mode 100644 templates/blog.html create mode 100644 templates/heaven.html create mode 100644 templates/hell.html create mode 100644 templates/index.html create mode 100644 templates/macros.html create mode 100644 templates/page.html create mode 100644 templates/post.html create mode 100644 templates/section.html create mode 100644 templates/shortcodes/age.html create mode 100644 templates/shortcodes/album_list.html create mode 100644 templates/shortcodes/soaosed.html create mode 100644 templates/shortcodes/subtree.html create mode 100644 templates/shortcodes/title_bar.html create mode 100644 templates/shortcodes/tree_view.html create mode 100644 templates/shortcodes/treelink.html create mode 100644 templates/shortcodes/window.html create mode 100644 templates/shortcodes/window_body.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d70ebaa --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +public \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e04a1e9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "grammarly.selectors": [ + { + "language": "markdown", + "scheme": "file" + } + ] +} \ No newline at end of file diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..4a54f74 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +soaos.dev diff --git a/README.md b/README.md new file mode 100644 index 0000000..a611c51 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# soaos.dev source files + +this site is generated using the zola ssg \ No newline at end of file diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..dd2361a --- /dev/null +++ b/config.toml @@ -0,0 +1,23 @@ +# The URL the site will be built for +base_url = "https://soaos.dev" + +title = "soaos" +description = "i forgot to write a description for this post" +default_language = "en" +author = "soaos" + +compile_sass = false +build_search_index = false +generate_feeds = true +generate_robots_txt = true +generate_sitemap = true + +[markdown] + +external_links_target_blank = true + +[markdown.highlighting] +theme = "everforest-dark" + +[extra] +# Put all your custom variables here diff --git a/content/_index.md b/content/_index.md new file mode 100644 index 0000000..c7b5757 --- /dev/null +++ b/content/_index.md @@ -0,0 +1,4 @@ ++++ +title = "soaos" +template = "index.html" ++++ diff --git a/content/about.md b/content/about.md new file mode 100644 index 0000000..6e1cc69 --- /dev/null +++ b/content/about.md @@ -0,0 +1,18 @@ +--- +title: "about" +template: "about.html" +--- + +Photo of me Hey, I'm {% soaosed() %}soaos{% end %}, a software developer and "artist". + +I'm currently living in Waterloo, Ontario. I went to college here and decided to stick around because I wuv it so much. + +## Some Stuff I Like + +### 🎶 Music + +I listen to a lot of different kinds of (rock) music. Recently I've been listening to a lot of post-hardcore and post-rock. + +Here are some albums I like: +{{ album_list() }} + diff --git a/content/blog/_index.md b/content/blog/_index.md new file mode 100644 index 0000000..44bbfa0 --- /dev/null +++ b/content/blog/_index.md @@ -0,0 +1,6 @@ ++++ +title = "soaos blog" +template = "blog.html" +page_template = "post.html" +sort_by = "date" ++++ \ No newline at end of file diff --git a/content/blog/blog.css b/content/blog/blog.css new file mode 100644 index 0000000..372964d --- /dev/null +++ b/content/blog/blog.css @@ -0,0 +1,99 @@ +html { + background: url("/assets/bg.jpg"); + background-attachment: fixed; + background-size: cover; + image-rendering: pixelated; +} + +figure { + background-color: var(--bg0); + margin: 0; + margin-bottom: 1em; +} + +figure > img, figure > * > img, figure > svg, figure > * svg { + background-color: var(--bg-dim); +} + +.cover-image > img { + width: 100%; + height: auto; + padding: 0 50px; + display: block; + box-sizing: border-box; +} + +figcaption { + padding: 0.5em; + font-style: italic; +} + +body { + max-width: 800px; + margin: 1em auto; + background-color: var(--bg1); +} + +.text-section { + padding: 1em; +} + +.fig { + margin: 0.5em; +} + +.fig-horizontal { + width: min-content; + margin: 0.5em auto; +} + +.fig-right { + float: right; + width: min-content; +} + +.fig-left { + float: left; + width: min-content; +} + + +.fig > img, .fig > * > img { + padding: 0.5em; +} + +pre { + padding: 1em 0.5em; + margin: 0; + overflow: scroll; + background-color: var(--bg-dim); +} + +code { + background-color: var(--bg-dim); +} + +table.schema-table { + border-collapse: collapse; + display: inline-table; + margin-bottom: 0.5em; + background-color: var(--bg-dim); +} + +.schema-table th { + background-color: var(--bg0); +} + +.schema-table th,td { + border: 2px solid var(--bg2); + padding: 0.25em; +} + + + +@media (max-width: 700px) { + .fig-left, .fig-right { + float: unset; + margin: 0.5em auto; + } +} \ No newline at end of file diff --git a/content/blog/rockbox_stats/index.md b/content/blog/rockbox_stats/index.md new file mode 100644 index 0000000..626cbe9 --- /dev/null +++ b/content/blog/rockbox_stats/index.md @@ -0,0 +1,547 @@ ++++ +title = "Rockbox Stat Tracking" +date = "2025-09-02" ++++ + +
+
+
+

In this post I talk about how I went about setting up a stat visualization page for my rockbox mp3 player.

+
+
+ + + + + + + + + + + + + + + + + + +Progressive Rock +Alternative +Post-Rock +Post-Hardcore +Post-Metal +Rock +Shoegaze +Progressive Metal + + +
A static site generation experiment
+
+
+
+

Preamble: Digital Sovereignity & Rockbox

+

+ I've been building up a pretty sizeable collection of digital music + over the last couple of years. I think there's a lot of value in owning + the music I pay for and being able to choose how I listen to it. + Purchasing music also allows me to support artists in a more direct + and substantial way than the fractions of cents for using streaming services, + but that's more of a happy consequence than some moral obligation I feel. +

+

+ Over the years, I've enjoyed listening to my music in a variety of ways. + For years I kept all of my music files on all of my devices and used + various local music clients depending on the platform, most notably mpd + and ncmpcpp on linux. Eventually, as I charged headlong into the glorious + world of self-hosting, I began using a central Jellyfin media server that + I stream music and video from. It's super convenient, and works on all of + my devices (including my TV!). +

+

+ My media server is great, and it's been the primary way I listen to music + for a while now. But it has limitations. For example, I don't expose my media + server to the internet, so I'm unable to stream from it while I'm out and + about. And even if I could, the bandwidth requirements would be pretty high. + I figured I would need a dedicated music player if I wanted to take my music + library on the go, and settled on the HIFI Walker H2 after reading some + online recommendations. The ability to install Rockbox, an open-source firmware, + was a big factor in my decision. I couldn't tell you how the device works + out of the box, since I flashed the firmware pretty much immediately once I got it, + but I've been super impressed with how the device works while running Rockbox. +

+

+

+ Screenshot of Rockbox player showing cool theme. +
I'm using a modified version of the InfoMatrix-v2 theme, which looks great.
+
+ Rockbox comes with many codecs for common audio formats including FLAC and MP3. The + device boots extremely quickly, and the interface is snappy. Virtually every aspect + of the user experience is tweakable and customizable to a crazy degree. I've even begun + listening to music on my player even at home, since a device specifically for the + purpose provides less distraction while I'm trying to be productive. +

+

+ All this to say I'm pretty much sold on Rockbox. But there's certain things I + still miss from my days of being a user of popular services like Spotify with + fancy APIs and data tracking. Things like Spotify wrapped or third-party apps + for visualizing playback statistics are a fun way to see what my listening history + looks like and could potentially be used to help find more music that I'd enjoy. + This is why when I noticed that Rockbock has a playback logging feature, a little + lightbulb lit up over my head. +

+
+
+

Generating and Parsing Logs

+

+

+ Logging +
The logging feature can be accessed through the settings menu.
+
+ Rockbox has a feature that logs playback information to a text file. This feature can + be enabled by setting Playback Settings > Logging to "On". With this setting enabled, a + new line gets added to the end of the .rockbox/playback.log file every time you play a track, + containing info about what you played and when. +

+

+ The logging feature is actually already used by the LastFM scrobbler plugin that comes preloaded with + Rockbox, which is probably the simplest way to get insights into your playback. However, + I personally want to avoid using third-party services as much as possible, because it's more fun. +

+

+ If I take a look at a logfile generated after a bit of listening, I'll see that I've wound up with + a series of lines that each look something like this: +

+
1758478258:336689:336718:/<microSD0>/Music/This Is The Glasshouse/This Is The Glasshouse - 867/This Is The Glasshouse - 867 - 01 Streetlight By Streetlight.flac
+
An example of a log entry for "Streetlight by Streetlight" by This is the Glasshouse. +
+
+

+

+ I wasn't really able to find any information online about the format of these logs, but they appear + to be simple enough to figure out. From what I can tell, each event is broken up into 4 pieces: +

    +
  1. Timestamp: The number of milliseconds since the UNIX epoch. +
  2. Playback Duration: The amount of the song that was played, in milliseconds. +
  3. Total Track Length: The length of the played track, in milliseconds. +
  4. File Path: An absolute path to the file containing the track on the filesystem. +
+ All of this is enough to know what I was listening to and when. I can use the file path to check for + audio tags which can help glean even more information about my listening habits. +

+

Now that I have this information and know how to interpret it, I'm ready to start processing it!

+
+
+

Analyzing Playback History

+

+ In order to get some useful information out of my playback history, I think it's a good idea to start by + building + a database. I created a sqlite database with the following tables: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
songs
idi64PK
titleString
artistsJSON
album_idi64?
genreString?
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
albums
idi64PK
titleString
artistString
cover_artBlob?
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
history
idi64PK
timestampDatetime
durationi64
song_idi64
+
+
+ I can add more columns later, but this is a good place to start. +

+

+ Now, as I read through the logfile line-by-line, I can check if each album exists before + inserting it into the database: +

+
for line in log_file.lines().flatten() {
+println!("{line}");
+// Skip comments
+if line.starts_with("#") {
+continue;
+}
+let chunks = line.split(":").collect::>();
+
+let timestamp = DateTime::from_timestamp_secs(
+i64::from_str_radix(chunks[0], 10).context("Failed to parse timestamp")?,
+)
+.context("Failed to convert timestamp")?;
+
+// Load tags from file on device
+let file_path = chunks[chunks.len() - 1][1..]
+.split_once("/")
+.context("Missing file")?
+.1;
+let tags = Tag::new()
+.read_from_path(args.mount_point.join(file_path))
+.context("Failed to read audio tags")?;
+
+//...
+}
+
Parsing log entry and loading audio metadata.
+
+
+
if let Some(existing_album) =
+sqlx::query("SELECT id FROM albums WHERE title=$1 AND artist=$2")
+.bind(album_title)
+.bind(album_artist)
+.fetch_optional(&mut *db)
+.await
+.context("Failed to execute query to find existing album")?
+{
+let album_id: i64 = existing_album.get("id");
+info!("Album already exists, id {album_id}");
+//...
+} else {
+info!("Inserting new album: {album_title} by {album_artist}");
+//...
+let result = sqlx::query(
+"INSERT INTO albums (title, artist, cover_art) VALUES ($1, $2, $3);",
+)
+.bind(album_title)
+.bind(album_artist)
+.bind(cover)
+.execute(&mut *db)
+.await
+.context("Failed to execute query to insert album into database")?;
+
+//...
+}
+
Checking for an album with matching artist and title before creating a new row in the + database.
+
+ I did something similar with the songs and history tables, basically building up a cache + of history information and skipping anything that's already in the database on repeat runs. +

+

+ With this database constructed, it's pretty easy to get a bunch of different information + about my listening. For example (forgive me if my SQL skills are kind of ass lol): +

+
SELECT 
+songs.title AS song_title,
+songs.artists AS song_artists,
+songs.genre AS song_genre,
+albums.title AS album_title,
+albums.artist AS album_artist,
+history.timestamp AS timestamp,
+history.duration AS duration
+FROM history
+CROSS JOIN songs ON songs.id = history.song_id
+CROSS JOIN albums ON albums.id = songs.album_id
+ORDER BY timestamp DESC;
+
Querying for a list of each history entry along with track metadata, sorted from most to + least recent.
+
+
+
SELECT 
+songs.genre,
+SUM(history.duration) AS total_duration
+FROM history
+CROSS JOIN songs ON history.song_id = songs.id
+GROUP BY genre
+ORDER BY total_duration DESC
+LIMIT 10; 
+
Querying for the top 10 most listened genres by playtime.
+
+

+

+ It's all well and good to be able to view this information using a database client, + but it would be really cool if I could visualize this data somehow. +

+
+
+

Visualizing this Data Somehow

+

+ I wanted to make this data available on my website for people to view, and for a bunch of mostly trivial + reasons I won't get into here, I have a couple of requirements for pages on this site: +

    +
  1. Pages need to be static. +
  2. Pages need to be JavaScript-free. +
+ This means any chart rendering needs to be done automatically at build time before + deploying. I don't currently use a static site generator for my site (just for fun), + so I'm basically going to need to write one specifically to generate this page. +

+

+ I won't get too deep into the specifics of how I queried the database and generated each visualization + on + the page, but I can explain the visualizations I created using the queries from the previous section. + For the + listening history I wanted to generate a table displaying the information. To accomplish this, I first + used a combination of sqlx's ability to convert a row to a struct and serde to serialize + the rows as JSON values. +

+
#[derive(Serialize, Deserialize, FromRow)]
+struct HistoryEntry {
+song_title: String,
+song_artists: Value,
+timestamp: DateTime<Utc>,
+duration: i64,
+album_title: String,
+album_artist: Option<String>,
+song_genre: Option<String>,
+}
+
+//...later
+let history = sqlx::query_as::<_, HistoryEntry>(
+/* SELECT... */
+).fetch_all(&mut *db).await;
+
+//...later still, tera context accepts
+let mut context = tera::Context::new();
+context.insert("history", &history);
+
+
Struct definition for a history entry, allowing conversion from a sqlx row and + de/serialization from/to JSON.
+
+

+

+ In order to keep the generation as painless as possible, I decided to use the Tera template + engine, which allows me to define a template HTML file and substitute in values from + a context which I can define before rendering. In the case of the table, I can just generate a <tr> + matching the data for each item: +

+
{% macro history_table(history) %}
+<h3>Playback History</h3>
+<div class="table-container">
+<table>
+<thead>
+    <tr>
+        <th>Timestamp</th>
+        <th>Played Duration</th>
+        <th>Title</th>
+        <th>Artists</th>
+        <th>Album</th>
+        <th>Genre</th>
+    </tr>
+</thead>
+<tbody>
+    {% for item in history %}<tr>
+        <td>{{ item.timestamp | date(format="%Y-%m-%d %H:%M:%S") }}</td>
+        <td>{{ item.duration | hms }}</td>
+        <td>{{ item.song_title }}</td>
+        <td>{{ item.song_artists }}</td>
+        <td>{{ item.album_title }}</td>
+        <td>{{ item.song_genre }}</td>
+    </tr>
+    {% endfor %}
+</tbody>
+</table>
+</div>
+{% endmacro history_table %}
+
+ A Tera macro for generating a table from a list of playback history items. + I used a macro so I can re-use this later if I want to add time range views. + (last month, year, etc.) +
+
+

+

+ I wrote similar macros for each of the visualizations I wanted to create. Most are + easy, but for my top 10 genres I wanted to display a pie chart. I found a pretty decent + data visualization crate called charming that's able to render to html, however + the output contains javascript so it's a no-go for me. Luckily, it can also render to + an SVG which I can embed nicely within the page. +

+ + + + + + + + + + + + + + + + + + +Progressive Rock +Alternative +Post-Rock +Post-Hardcore +Post-Metal +Rock +Shoegaze +Progressive Metal + + +
Here's one I generated just now.
+
+

+

+ And that's pretty much all there is to it! The finished thing can be found here. +

+
+
\ No newline at end of file diff --git a/content/blog/rockbox_stats/log-setting.bmp b/content/blog/rockbox_stats/log-setting.bmp new file mode 100644 index 0000000..29789fa Binary files /dev/null and b/content/blog/rockbox_stats/log-setting.bmp differ diff --git a/content/blog/rockbox_stats/playback-settings.bmp b/content/blog/rockbox_stats/playback-settings.bmp new file mode 100644 index 0000000..cee3cfb Binary files /dev/null and b/content/blog/rockbox_stats/playback-settings.bmp differ diff --git a/content/blog/rockbox_stats/player.bmp b/content/blog/rockbox_stats/player.bmp new file mode 100644 index 0000000..452a057 Binary files /dev/null and b/content/blog/rockbox_stats/player.bmp differ diff --git a/content/blog/terminal_renderer_mkii/cover.png b/content/blog/terminal_renderer_mkii/cover.png new file mode 100644 index 0000000..b3ddfd9 Binary files /dev/null and b/content/blog/terminal_renderer_mkii/cover.png differ diff --git a/content/blog/terminal_renderer_mkii/david.png b/content/blog/terminal_renderer_mkii/david.png new file mode 100644 index 0000000..6cfa884 Binary files /dev/null and b/content/blog/terminal_renderer_mkii/david.png differ diff --git a/content/blog/terminal_renderer_mkii/davidbayer.png b/content/blog/terminal_renderer_mkii/davidbayer.png new file mode 100644 index 0000000..af4bfc4 Binary files /dev/null and b/content/blog/terminal_renderer_mkii/davidbayer.png differ diff --git a/content/blog/terminal_renderer_mkii/davidthreshold.png b/content/blog/terminal_renderer_mkii/davidthreshold.png new file mode 100644 index 0000000..6c6e014 Binary files /dev/null and b/content/blog/terminal_renderer_mkii/davidthreshold.png differ diff --git a/content/blog/terminal_renderer_mkii/index.md b/content/blog/terminal_renderer_mkii/index.md new file mode 100644 index 0000000..1797a4a --- /dev/null +++ b/content/blog/terminal_renderer_mkii/index.md @@ -0,0 +1,167 @@ ++++ +title = "Terminal Renderer Mk. II - Rendering to Text with Compute" +date = "2025-10-02" ++++ +
+
+

This week I brought my terminal renderer to the next level by performing text rendering on the GPU. +

+
+
+ +
The Stanford Dragon, outlined and rendered as Braille characters in a terminal emulator. +Full video +
+
+
+
+

Context

+

Unicode Braille

+

+I first messed around with rendering images to the terminal with Braille characters in like 2022 I +think? I wrote a simple CLI tool +that applied a threshold to an input image and output it as Braille characters in the terminal. Here's a recording I took back + when I did it. +

+ +

+

+
+ + + + + + + + + + + + + + + + + + + +
03
14
25
67
+
+
The corresponding bit position for each braille dot.
+
+This effect is pretty cool, and it was pretty easy to implement as well. The trick lies in how the +Unicode Braille block +is laid out. Every 8-dot Braille combination happens to add up to 256 combinations, the perfect amount to +fit in the range between 0x2800 (⠀) and 0x28FF (⣿). In other words, every +character +within the block can be represented by changing the value of a single byte. +

+

+The lowest 6 bits of the pattern map on to a 6-dot braille pattern. However, due +to historical reasons the 8-dot values were tacked on after the fact, which adds +a slightly annoying mapping to the conversion process. Either way, it's a lot easier +than it could be to just read a pixel value, check its brightness, and then use a +bitwise operation to set/clear a dot. +

+

Ordered Dithering

+

+Comparing the brightnes of a pixel against a constant threshold is a fine way to +display black and white images, but it's far from ideal and often results in the loss +of a lot of detail from the original image. +

+
+
+ + + +
+
From left to right: Original image, threshold, and ordered dither. Wikipedia
+
+

By using ordered dithering, +we +can preserve much more of the subtleties of the original image. While not the "truest" version of +dithering possible, +ordered dithering (and Bayer dithering in particular) provides a few advantages that make it very +well suited to realtime computer graphics: +

+Feel free to read up on the specifics of threshold maps and stuff, but for the purposes of this little +explanation it's +enough to know that it's basically just a matrix of 𝓃⨉𝓃 values between 0 and 1, and then to determine +whether a pixel (𝓍,𝓎) +is white or black, you check the brightness against the threshold value at (𝓍%𝓃,𝓎%𝓃) in the map. +

+
+
+

The old way™

+

+My first attempt at realtime terminal graphics with ordered dithering +(I put a video up at the time) +ran entirely on the CPU. I pre-calculated the threshold map at the beginning of execution and ran each +frame +through a sequential function to dither it and convert it to Braille characters. +

+

+To be honest, I never noticed +any significant performance issues doing this, as you can imagine the image size required to fill a +terminal +screen is signficantly smaller than a normal window. However, I knew I could easily perform the +dithering on the GPU +as a post-processing effect, so I eventually wrote a shader to do that. In combination with another +effect I used to +add outlines to objects, I was able to significantly improve the visual fidelity of the experience. A +good example of +where the renderer was at until like a week ago can be seen in this video. +

+

+Until now I hadn't really considered moving the text conversion to the GPU. I mean, GPU is for +graphics, +right? I just copied the entire framebuffer back onto the CPU after dithering +and used the same sequential conversion algorithm. Then I had an idea that would drastically reduce the +amount +of copying necessary. +

+
+
+

Compute post-processing

+

+What if, instead of extracting and copying the framebuffer every single frame, we "rendered" the text on +the GPU +and read that back instead? Assuming each pixel in a texture is 32 bits (RGBA8), and knowing that +each braille +character is a block of 8 pixels, could we not theoretically shave off at least 7/8 of the bytes +copied? +

+

+As it turns out, it's remarkably easy to do. I'm using the Bevy engine, +and hooking in a compute node to my existing post-processing render pipeline worked right out of the +box. +I allocated a storage buffer large enough to hold the necessary amount of characters, read it back each +frame, and dumped +the contents into the terminal. +

+

+I used UTF-32 encoding on the storage buffer because I knew I could easily convert a "wide string" into +UTF-8 before printing it, and +32 bits provides a consistent space to fill for each workgroup in the shader versus a variable-length + encoding like UTF-8. Here's a video of the new renderer working. +Although now that I think about it, I could probably switch to using UTF-16 since all the Braille +characters could be represented +in 2 bytes, and that would be half the size of the UTF-32 text, which is half empty bytes anyways. +

+

+Okay so I went and tried that but remembered that shaders only accept 32-bit primitive types, so it doesn't matter anyways. This little side quest has been a part of my +broader efforts to revive a project I +spent a lot of time on. I'm taking the opportunity to really dig in and rework some of the stuff I'm not +totally happy with. So there might be quite a few of this kind of post in the near future. Stay tuned. +

+
diff --git a/content/blog/vscode_buttplug/buttplug-0.0.1.vsix b/content/blog/vscode_buttplug/buttplug-0.0.1.vsix new file mode 100644 index 0000000..f57f8d3 Binary files /dev/null and b/content/blog/vscode_buttplug/buttplug-0.0.1.vsix differ diff --git a/content/blog/vscode_buttplug/index.md b/content/blog/vscode_buttplug/index.md new file mode 100644 index 0000000..6abe64d --- /dev/null +++ b/content/blog/vscode_buttplug/index.md @@ -0,0 +1,63 @@ ++++ +title = "Visual Studio Code Buttplug Integration" +description = "Vibe coding." +date = "2026-01-29" ++++ + +[tl;dr: download VSIX here lol](./buttplug-0.0.1.vsix) + +This is perhaps one of the most important project ideas I've come up with in a while. I pounded a redbull at like 8pm and then an angel of the Lord appeared to me and said "Man, it would be really funny if you added buttplug support to vscode". So I did. I was inspired by various buttplug mods for random games that probably have no business having buttplug mods, like Ultrakill's [UKButt](https://github.com/PITR-DEV/ukbutt-mod) mod and the pioneering mind who made one for [Webfishing](https://github.com/elliotcubit/WebfishingButtplug). + +I've been wanting to dip my toes into VS Code extension development for a while, and something like buttplug integration seemed like an interesting step up from a basic hello world. Plus I get to be the guy who made a buttplug extension for a text editor, and bring that up at networking events, parties, and job interviews. + +So you might be wondering what the hell "buttplug integration" actually means. As it turns out, ass hardware is pretty advanced these days, with all kinds of bluetooth doohickeys that irradiate your insides and connect to your Google Home to your prostate and shit. Naturally, open source protocols for interfacing with smart sex toys have also emerged. [Buttplug.io](https://buttplug.io/) seems like a pretty popular choice for this, and it's actually pretty easy to work with. + +That still leaves the question of what buttplug integration should look like for a text editor. This is a subject that invites rigorous debate. I was looking at this from a very practical standpoint, so my main goal was to provide developers with an enhanced level of immersion that would boost their coding productivity. When I think of immersion, I think of the main document I'm editing. I want to *feel* the code as I'm writing it... in my ass. So that's where I started: + +```ts +// Vibe on type +context.subscriptions.push(vscode.workspace.onDidChangeTextDocument(async (e) => { + if (config!.get('typingStrength') === 0) { return; } + //... + const activeEditor = vscode.window.activeTextEditor; + + if (!activeEditor) { + return; + } + + await selected_device?.vibrate((config!.get('typingStrength') as number) * (config!.get('strength') as number)); + if (timeout) { + clearTimeout(timeout); + } + timeout = setTimeout(async () => await selected_device?.stop(), config!.get('typingDuration') as number * 1000); +})); +``` +(The full code for this extension is available [here](https://git.soaos.dev/vscode-buttplug.git/tree/).) + +Basically what this snippet does is watches for any edits to the currently open text editor and provides "haptic feedback" for each keystroke. +I made the strength and duration of these vibrations, along with an overall strength multiplier, configurable by the user. + +This on its own would be great, but it isn't enough. This extension needs to be a holistic overhaul of the modern developer experience. And as modern developers, actually writing code takes up such a miniscule amount of our time. Sitting on your ass waiting for your insanely bloated app to build is a much more important activity, and one that is normally very boring. Well, no more: + +```ts +// Vibrate continuously while building +context.subscriptions.push(vscode.tasks.onDidStartTask(async e => { + if (config!.get('buildStrength') === 0) { return; } + if (e.execution.task.group === vscode.TaskGroup.Build) { + building = true; + await selected_device?.vibrate((config!.get('buildStrength') as number) * (config!.get('strength') as number)); + } +})); + +// Stop vibration once build tasks all finish +context.subscriptions.push(vscode.tasks.onDidEndTask(async e => { + if ((vscode.tasks.taskExecutions).every(t => t.task.group !== vscode.TaskGroup.Build)) { + building = false; + await selected_device?.stop(); + } +})); +``` + +The extension obliterates your asshole relentlessly while a build task is in progress (sorry rust devs, although I have a feeling you won't mind). This is extremely useful, since the vibration only stops once all build steps are complete, letting you know it's ok to stop scrolling Instagram reels and get back to work. + +That covers the basic functionality of the extension, outside of a few utility commands like a killswitch. If you want to try it out, just download the [intiface central](https://intiface.com/central/) app, install the extension, connect and install your hardware, and godspeed. \ No newline at end of file diff --git a/content/heaven/_index.md b/content/heaven/_index.md new file mode 100644 index 0000000..ce3b970 --- /dev/null +++ b/content/heaven/_index.md @@ -0,0 +1,4 @@ ++++ +title = "heaven" +template = "heaven.html" ++++ \ No newline at end of file diff --git a/content/heaven/angel.gif b/content/heaven/angel.gif new file mode 100644 index 0000000..1c591e8 Binary files /dev/null and b/content/heaven/angel.gif differ diff --git a/content/heaven/angel2.gif b/content/heaven/angel2.gif new file mode 100644 index 0000000..54adc92 Binary files /dev/null and b/content/heaven/angel2.gif differ diff --git a/content/heaven/angel3.gif b/content/heaven/angel3.gif new file mode 100644 index 0000000..e6aedcb Binary files /dev/null and b/content/heaven/angel3.gif differ diff --git a/content/heaven/bg.jpg b/content/heaven/bg.jpg new file mode 100644 index 0000000..91a9238 Binary files /dev/null and b/content/heaven/bg.jpg differ diff --git a/content/heaven/everytime_we_touch_nightcore.ogg b/content/heaven/everytime_we_touch_nightcore.ogg new file mode 100644 index 0000000..3ac875d Binary files /dev/null and b/content/heaven/everytime_we_touch_nightcore.ogg differ diff --git a/content/heaven/heaven.css b/content/heaven/heaven.css new file mode 100644 index 0000000..5968745 --- /dev/null +++ b/content/heaven/heaven.css @@ -0,0 +1,24 @@ +html { + background: url("bg.jpg"); + background-attachment: fixed; + background-size: cover; + image-rendering: pixelated; +} + +#heavenly-host { + width: 100%; + height: 100%; + position: fixed; + top: 0; + left: 0; + z-index: -999; + background-image: url("angel.gif"); + background-size: 128px 128px; + opacity: 0.25; + --pan: 128px -128px; + animation: pan linear 3s infinite; +} + +.section { + background-color: rgba(from var(--bg-yellow) r g b / 0.5); +} diff --git a/content/hell/Flying_Skeleton_Hell.gif b/content/hell/Flying_Skeleton_Hell.gif new file mode 100644 index 0000000..7cfd429 Binary files /dev/null and b/content/hell/Flying_Skeleton_Hell.gif differ diff --git a/content/hell/_index.md b/content/hell/_index.md new file mode 100644 index 0000000..310e63e --- /dev/null +++ b/content/hell/_index.md @@ -0,0 +1,70 @@ ++++ +title = "soaos" +template = "hell.html" ++++ + +
+
+ + (please enable autoplay to hear music lol) + + +
+

👿 WELLCOME INTO HELL... YOU SUCKER!!!!! 👿

+
+ +
+
+
+
+ +

+ Hey there pal. It's me haha... the "big" "guy"... AKA satan... I + hope you like it here in hell, i worked hard on making it evil :D +

+

+ idk what you did to wind up here, but now you're stuck here... + forever... with me haha ;) +

+

+ so... Make yourself comfortable... haha if you can lol >:] +

+

+ AND DON'T EFFING TRY ESCAPING!!! }:[ alright lucifer out +

+ +
+ +
+ + +
+
+
+
+ + + + + + + + + + +
+
+F*CKSCREW YOU, SUCKER 💔🥀 +
+
diff --git a/content/hell/bg.jpg b/content/hell/bg.jpg new file mode 100644 index 0000000..8d0fad5 Binary files /dev/null and b/content/hell/bg.jpg differ diff --git a/content/hell/bigguy.gif b/content/hell/bigguy.gif new file mode 100644 index 0000000..c3314c7 Binary files /dev/null and b/content/hell/bigguy.gif differ diff --git a/content/hell/comunismo.gif b/content/hell/comunismo.gif new file mode 100644 index 0000000..18da593 Binary files /dev/null and b/content/hell/comunismo.gif differ diff --git a/content/hell/demon.gif b/content/hell/demon.gif new file mode 100644 index 0000000..a5868d2 Binary files /dev/null and b/content/hell/demon.gif differ diff --git a/content/hell/demon2.gif b/content/hell/demon2.gif new file mode 100644 index 0000000..ebbd919 Binary files /dev/null and b/content/hell/demon2.gif differ diff --git a/content/hell/demon3.gif b/content/hell/demon3.gif new file mode 100644 index 0000000..fc73814 Binary files /dev/null and b/content/hell/demon3.gif differ diff --git a/content/hell/demon4.gif b/content/hell/demon4.gif new file mode 100644 index 0000000..debe626 Binary files /dev/null and b/content/hell/demon4.gif differ diff --git a/content/hell/demon_face.gif b/content/hell/demon_face.gif new file mode 100644 index 0000000..5c71e60 Binary files /dev/null and b/content/hell/demon_face.gif differ diff --git a/content/hell/evilmind.gif b/content/hell/evilmind.gif new file mode 100644 index 0000000..1b6cb9b Binary files /dev/null and b/content/hell/evilmind.gif differ diff --git a/content/hell/evilorb.gif b/content/hell/evilorb.gif new file mode 100644 index 0000000..003ef97 Binary files /dev/null and b/content/hell/evilorb.gif differ diff --git a/content/hell/firebreak.gif b/content/hell/firebreak.gif new file mode 100644 index 0000000..981f1bf Binary files /dev/null and b/content/hell/firebreak.gif differ diff --git a/content/hell/gay.gif b/content/hell/gay.gif new file mode 100644 index 0000000..156bbea Binary files /dev/null and b/content/hell/gay.gif differ diff --git a/content/hell/gay2.gif b/content/hell/gay2.gif new file mode 100644 index 0000000..1a00852 Binary files /dev/null and b/content/hell/gay2.gif differ diff --git a/content/hell/gaydudes.gif b/content/hell/gaydudes.gif new file mode 100644 index 0000000..65c4ed3 Binary files /dev/null and b/content/hell/gaydudes.gif differ diff --git a/content/hell/hell.ogg b/content/hell/hell.ogg new file mode 100644 index 0000000..0abf641 Binary files /dev/null and b/content/hell/hell.ogg differ diff --git a/content/hell/hellisreal.gif b/content/hell/hellisreal.gif new file mode 100644 index 0000000..acbd894 Binary files /dev/null and b/content/hell/hellisreal.gif differ diff --git a/content/hell/hitler.gif b/content/hell/hitler.gif new file mode 100644 index 0000000..4edc5d8 Binary files /dev/null and b/content/hell/hitler.gif differ diff --git a/content/hell/hitler2.gif b/content/hell/hitler2.gif new file mode 100644 index 0000000..7fd7132 Binary files /dev/null and b/content/hell/hitler2.gif differ diff --git a/content/hell/hitler3.gif b/content/hell/hitler3.gif new file mode 100644 index 0000000..8edd36c Binary files /dev/null and b/content/hell/hitler3.gif differ diff --git a/content/hell/hot.gif b/content/hell/hot.gif new file mode 100644 index 0000000..4c1660f Binary files /dev/null and b/content/hell/hot.gif differ diff --git a/content/hell/kissing.jpg b/content/hell/kissing.jpg new file mode 100644 index 0000000..b6190c6 Binary files /dev/null and b/content/hell/kissing.jpg differ diff --git a/content/hell/obama.gif b/content/hell/obama.gif new file mode 100644 index 0000000..4f42d27 Binary files /dev/null and b/content/hell/obama.gif differ diff --git a/content/hell/pitchfork.gif b/content/hell/pitchfork.gif new file mode 100644 index 0000000..ab880fa Binary files /dev/null and b/content/hell/pitchfork.gif differ diff --git a/content/hell/redfire.gif b/content/hell/redfire.gif new file mode 100644 index 0000000..8f15a7a Binary files /dev/null and b/content/hell/redfire.gif differ diff --git a/content/hell/skull.gif b/content/hell/skull.gif new file mode 100644 index 0000000..89ed718 Binary files /dev/null and b/content/hell/skull.gif differ diff --git a/content/hell/smallfire.gif b/content/hell/smallfire.gif new file mode 100644 index 0000000..7b29fdf Binary files /dev/null and b/content/hell/smallfire.gif differ diff --git a/content/hell/torch.gif b/content/hell/torch.gif new file mode 100644 index 0000000..c06066b Binary files /dev/null and b/content/hell/torch.gif differ diff --git a/content/projects/bevy_plugins/index.html b/content/projects/bevy_plugins/index.html new file mode 100644 index 0000000..5f5c427 --- /dev/null +++ b/content/projects/bevy_plugins/index.html @@ -0,0 +1,5 @@ + + + + + diff --git a/content/projects/games/NIX_AVREA/index.html b/content/projects/games/NIX_AVREA/index.html new file mode 100644 index 0000000..47c914b --- /dev/null +++ b/content/projects/games/NIX_AVREA/index.html @@ -0,0 +1,26 @@ + + + + + + + NIX AVREA + + + + Go Home + Go Back +

NIX AVREA

+

This is a project I've been working on since April 2024. It's probably the longest-running personal project I've ever done and has been a monumental undertaking so far.

+

I'm unsure how much I want to reveal about this project while I'm developing it, I want the experience to be as novel as possible once it's out. I think I'll probably stick to posting about it here on my site and the occasional YouTube video until it's closer to ready.

+

About the Project

+

NIX AVREA is the codename for my first game project. The game is highly experimental and features mechanics that (as far as I know) have never been attempted. The game is centered around dynamic content, using steganographic techniques to embed binary payloads inside of asset files in order to construct the game world from a directory on the player's filesystem.

+

There is a ton of stuff that's already implemented for this project and I'll gradually add more to the following directories explaining in-depth some of the components:

+
++-- mechanics/
++-- design/
++-- narrative/
+    
+ + + \ No newline at end of file diff --git a/content/projects/piss_daemon/index.html b/content/projects/piss_daemon/index.html new file mode 100644 index 0000000..5cb9481 --- /dev/null +++ b/content/projects/piss_daemon/index.html @@ -0,0 +1,43 @@ + + + + + + Piss Daemon - soaos + + + + Go Home + Go Back +

Piss Daemon

+

About

+

This is a D-Bus daemon (pissd) and client (piss-level) that monitor the international + space station's urine tank level.

+

I have it integrated into my status bar (X version):

+ A screenshot of an X11 statusbar showing the piss daemon piss level next to a toilet icon. +
+	#...
+
+	function piss {
+		PISS_LEVEL="$(piss-level)";
+		if [ -n "$PISS_LEVEL" ]; then
+			echo "  ${PISS_LEVEL}%";
+		fi;
+	}
+
+	#...
+
+	while true; do
+		xsetroot -name "$(piss)$(batt)$(datetime)";
+		sleep 1;
+	done;
+	
+

I made this pretty much entirely so that I could call a program "piss daemon".

+

External Links

+ + + + \ No newline at end of file diff --git a/content/projects/piss_daemon/statusbar.png b/content/projects/piss_daemon/statusbar.png new file mode 100644 index 0000000..b98e021 Binary files /dev/null and b/content/projects/piss_daemon/statusbar.png differ diff --git a/content/rockstats/index.html b/content/rockstats/index.html new file mode 100644 index 0000000..cfd916c --- /dev/null +++ b/content/rockstats/index.html @@ -0,0 +1,1301 @@ + + + + + + + + + + + + + + + + + +
+ ↰ Back + ⌂ Home +
+

Rockbox Stats

+

This page shows a bunch of information about the music I've been listening to on my mp3 player. Think of it kind of like my own personal Spotify wrapped, minus the antichrist.

+

This page is updated wehenever I feel like regenerating it.

+ +

Playback History

+

Total playtime: 7:57:48, 117 tracks

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TimestampPlayed DurationTitleArtistsAlbumGenre
2025-09-23 19:41:013:37The Misty Veil of May[Black Hill & Silent Island]Tales of the Night ForestPost-Rock
2025-09-23 19:37:242:26An Old Owl Calling[Black Hill & Silent Island]Tales of the Night ForestPost-Rock
2025-09-23 19:34:583:03Night in a Mossy Hut[Black Hill & Silent Island]Tales of the Night ForestPost-Rock
2025-09-23 19:31:552:31Crying Wind[Black Hill & Silent Island]Tales of the Night ForestPost-Rock
2025-09-23 19:29:233:05Dawn[Black Hill & Silent Island]Tales of the Night ForestPost-Rock
2025-09-23 19:26:173:27Hidden Valley[Black Hill & Silent Island]Tales of the Night ForestPost-Rock
2025-09-23 19:22:503:32The Gathering of Deer[Black Hill & Silent Island]Tales of the Night ForestPost-Rock
2025-09-23 19:19:173:49A Wild River to Take You Home[Black Hill & Silent Island]Tales of the Night ForestPost-Rock
2025-09-23 19:15:223:16A Wild River to Take You Home[Black Hill & Silent Island]Tales of the Night ForestPost-Rock
2025-09-23 16:05:052:49Twin Fantasy (Those Boys)[Car Seat Headrest]Twin FantasyAlternative
2025-09-23 15:52:1116:10Famous Prophets (Stars)[Car Seat Headrest]Twin FantasyAlternative
2025-09-23 15:36:017:39High to Death[Car Seat Headrest]Twin FantasyAlternative
2025-09-23 15:28:225:39Cute Thing[Car Seat Headrest]Twin FantasyAlternative
2025-09-23 15:22:426:46Bodys[Car Seat Headrest]Twin FantasyAlternative
2025-09-23 15:15:555:25Nervous Young Inhumans[Car Seat Headrest]Twin FantasyAlternative
2025-09-23 15:10:305:04Sober to Death[Car Seat Headrest]Twin FantasyAlternative
2025-09-23 15:05:251:29Stop Smoking (We Love You)[Car Seat Headrest]Twin FantasyAlternative
2025-09-23 15:03:5613:18Beach Life‐in‐Death[Car Seat Headrest]Twin FantasyAlternative
2025-09-23 14:50:372:52My Boy (Twin Fantasy)[Car Seat Headrest]Twin FantasyAlternative
2025-09-23 14:47:441:32The Light & the Glass[Coheed and Cambria]In Keeping Secrets of Silent Earth: 3Progressive Rock
2025-09-23 14:46:123:54A Favor House Atlantic[Coheed and Cambria]In Keeping Secrets of Silent Earth: 3Progressive Rock
2025-09-23 14:42:184:15The Camper Velourium III: Al the Killer[Coheed and Cambria]In Keeping Secrets of Silent Earth: 3Progressive Rock
2025-09-23 14:38:025:22The Camper Velourium II: Backend of Forever[Coheed and Cambria]In Keeping Secrets of Silent Earth: 3Progressive Rock
2025-09-23 14:32:405:21The Camper Velourium I: Faint of Hearts[Coheed and Cambria]In Keeping Secrets of Silent Earth: 3Progressive Rock
2025-09-23 14:27:184:05Blood Red Summer[Coheed and Cambria]In Keeping Secrets of Silent Earth: 3Progressive Rock
2025-09-23 14:23:136:35The Crowing[Coheed and Cambria]In Keeping Secrets of Silent Earth: 3Progressive Rock
2025-09-23 14:16:375:08Three Evils (Embodied in Love and Shadow)[Coheed and Cambria]In Keeping Secrets of Silent Earth: 3Progressive Rock
2025-09-23 14:11:295:00Cuts Marked in the March of Men[Coheed and Cambria]In Keeping Secrets of Silent Earth: 3Progressive Rock
2025-09-23 14:06:288:12In Keeping Secrets of Silent Earth: 3[Coheed and Cambria]In Keeping Secrets of Silent Earth: 3Progressive Rock
2025-09-23 13:49:392:07The Ring in Return[Coheed and Cambria]In Keeping Secrets of Silent Earth: 3Progressive Rock
2025-09-23 13:45:295:45Life and Death[The Dear Hunter]Act III: Life and DeathProgressive Rock
2025-09-23 13:39:433:25Father[The Dear Hunter]Act III: Life and DeathProgressive Rock
2025-09-23 13:36:172:16Son[The Dear Hunter]Act III: Life and DeathProgressive Rock
2025-09-23 13:34:013:15Go Get Your Gun[The Dear Hunter]Act III: Life and DeathProgressive Rock
2025-09-23 13:30:454:05This Beautiful Life[The Dear Hunter]Act III: Life and DeathProgressive Rock
2025-09-23 13:26:403:39He Said He Had a Story[The Dear Hunter]Act III: Life and DeathProgressive Rock
2025-09-23 13:23:014:41Saved[The Dear Hunter]Act III: Life and DeathProgressive Rock
2025-09-23 13:18:194:13Mustard Gas[The Dear Hunter]Act III: Life and DeathProgressive Rock
2025-09-23 13:14:065:01The Thief[The Dear Hunter]Act III: Life and DeathProgressive Rock
2025-09-23 13:09:054:51The Poison Woman[The Dear Hunter]Act III: Life and DeathProgressive Rock
2025-09-23 13:04:134:39The Tank[The Dear Hunter]Act III: Life and DeathProgressive Rock
2025-09-23 12:59:344:49What It Means to Be Alone[The Dear Hunter]Act III: Life and DeathProgressive Rock
2025-09-23 12:54:445:29In Cauda Venenum[The Dear Hunter]Act III: Life and DeathProgressive Rock
2025-09-23 12:49:151:38Writing on a Wall[The Dear Hunter]Act III: Life and DeathProgressive Rock
2025-09-23 12:47:367:09Vital Vessels Vindicate[The Dear Hunter]Act II: The Meaning of, & All Things Regarding Ms. LeadingProgressive Rock
2025-09-23 12:40:274:13Black Sandy Beaches[The Dear Hunter]Act II: The Meaning of, & All Things Regarding Ms. LeadingProgressive Rock
2025-09-23 12:36:134:28Dear Ms. Leading[The Dear Hunter]Act II: The Meaning of, & All Things Regarding Ms. LeadingProgressive Rock
2025-09-23 12:31:454:29Where the Road Parts[The Dear Hunter]Act II: The Meaning of, & All Things Regarding Ms. LeadingProgressive Rock
2025-09-23 12:27:156:07Red Hands[The Dear Hunter]Act II: The Meaning of, & All Things Regarding Ms. LeadingProgressive Rock
2025-09-23 12:22:051:13Red Hands[The Dear Hunter]Act II: The Meaning of, & All Things Regarding Ms. LeadingProgressive Rock
2025-09-23 12:15:593:48Blood of the Rose[The Dear Hunter]Act II: The Meaning of, & All Things Regarding Ms. LeadingProgressive Rock
2025-09-23 12:12:103:44Evicted[The Dear Hunter]Act II: The Meaning of, & All Things Regarding Ms. LeadingProgressive Rock
2025-09-23 12:08:264:45Smiling Swine[The Dear Hunter]Act II: The Meaning of, & All Things Regarding Ms. LeadingProgressive Rock
2025-09-23 12:03:407:46The Bitter Suite III: Embrace[The Dear Hunter]Act II: The Meaning of, & All Things Regarding Ms. LeadingProgressive Rock
2025-09-23 11:55:546:06The Bitter Suite I & II: Meeting Ms. Leading / Through the Dime[The Dear Hunter]Act II: The Meaning of, & All Things Regarding Ms. LeadingProgressive Rock
2025-09-23 11:49:474:57The Church & the Dime[The Dear Hunter]Act II: The Meaning of, & All Things Regarding Ms. LeadingProgressive Rock
2025-09-23 11:44:494:18The Oracles on the Delphi Express[The Dear Hunter]Act II: The Meaning of, & All Things Regarding Ms. LeadingProgressive Rock
2025-09-23 11:40:319:29The Lake and the River[The Dear Hunter]Act II: The Meaning of, & All Things Regarding Ms. LeadingProgressive Rock
2025-09-23 11:35:124:25The Lake and the River[The Dear Hunter]Act II: The Meaning of, & All Things Regarding Ms. LeadingProgressive Rock
2025-09-23 11:33:312:44The Lake and the River[The Dear Hunter]Act II: The Meaning of, & All Things Regarding Ms. LeadingProgressive Rock
2025-09-23 11:30:474:59The Procession[The Dear Hunter]Act II: The Meaning of, & All Things Regarding Ms. LeadingProgressive Rock
2025-09-23 11:25:570:38The Death and the Berth[The Dear Hunter]Act II: The Meaning of, & All Things Regarding Ms. LeadingProgressive Rock
2025-09-23 11:24:064:03The River North[The Dear Hunter]Act I: The Lake South, the River NorthProgressive Rock
2025-09-23 11:20:036:00His Hands Matched His Tongue[The Dear Hunter]Act I: The Lake South, the River NorthProgressive Rock
2025-09-23 11:14:026:00The Pimp and the Priest[The Dear Hunter]Act I: The Lake South, the River NorthProgressive Rock
2025-09-23 11:08:027:021878[The Dear Hunter]Act I: The Lake South, the River NorthProgressive Rock
2025-09-23 11:01:005:56The Inquiry of Ms. Terri[The Dear Hunter]Act I: The Lake South, the River NorthProgressive Rock
2025-09-23 10:55:035:56City Escape[The Dear Hunter]Act I: The Lake South, the River NorthProgressive Rock
2025-09-23 10:49:071:43The Lake South[The Dear Hunter]Act I: The Lake South, the River NorthProgressive Rock
2025-09-23 10:47:231:55Battesimo del fuoco[The Dear Hunter]Act I: The Lake South, the River NorthProgressive Rock
2025-09-23 10:44:364:24Chapter X[Sufferer]SuffererPost-Hardcore
2025-09-23 10:40:123:32Chapter IX[Sufferer]SuffererPost-Hardcore
2025-09-23 10:36:393:39Chapter VIII[Sufferer]SuffererPost-Hardcore
2025-09-23 10:33:006:08Chapter VII[Sufferer]SuffererPost-Hardcore
2025-09-23 10:26:513:45Chapter VI[Sufferer]SuffererPost-Hardcore
2025-09-23 10:23:063:39Chapter V[Sufferer]SuffererPost-Hardcore
2025-09-23 10:19:273:24Chapter IV[Sufferer]SuffererPost-Hardcore
2025-09-23 10:16:024:24Chapter III[Sufferer]SuffererPost-Hardcore
2025-09-23 10:11:372:28Chapter II[Sufferer]SuffererPost-Hardcore
2025-09-23 10:09:091:23Chapter I[Sufferer]SuffererPost-Hardcore
2025-09-21 19:50:5312:26Sleep[Godspeed You Black Emperor!]Lift Your Skinny Fists Like Antennas to HeavenPost-Rock
2025-09-21 19:50:183:27A Wild River to Take You Home[Black Hill & Silent Island]Tales of the Night ForestPost-Rock
2025-09-21 19:45:181:28A Wild River to Take You Home[Black Hill & Silent Island]Tales of the Night ForestPost-Rock
2025-09-21 19:43:490:28A Wild River to Take You Home[Black Hill & Silent Island]Tales of the Night ForestPost-Rock
2025-09-21 19:40:110:48Night Ela (Mystic Thing)[Candy Claws]Ceres & Calypso in the Deep TimeShoegaze
2025-09-21 19:28:371:28Night Ela (Mystic Thing)[Candy Claws]Ceres & Calypso in the Deep TimeShoegaze
2025-09-21 19:26:400:42The Tragedy[The Pax Cecilia]Blessed Are the BondsPost-Metal
2025-09-21 19:25:585:01A Dance With Death[We Lost the Sea]A Single FlowerPost-Rock
2025-09-21 19:25:003:26Homecoming: Denied![Harakiri for the Sky]Aokigahara MMXXIIPost-Metal
2025-09-21 19:21:331:18Keeping the Blade[Coheed and Cambria]Good Apollo I’m Burning Star IV, Volume One: From Fear Through the Eyes of MadnessProgressive Rock
2025-09-21 19:20:141:47I Existed[Snooze]I Know How You Will DieProgressive Metal
2025-09-21 19:14:227:40Robinson[This Is The Glasshouse]867Progressive Rock
2025-09-21 19:06:423:35October[This Is The Glasshouse]867Progressive Rock
2025-09-21 19:03:0710:21Old George[This Is The Glasshouse]867Progressive Rock
2025-09-21 18:52:459:34867[This Is The Glasshouse]867Progressive Rock
2025-09-21 18:43:113:38Two-Headed Calf[This Is The Glasshouse]867Progressive Rock
2025-09-21 18:39:336:257Bass / Lorne[This Is The Glasshouse]867Progressive Rock
2025-09-21 18:33:079:51January[This Is The Glasshouse]867Progressive Rock
2025-09-21 18:23:153:39Southpaw[This Is The Glasshouse]867Progressive Rock
2025-09-21 18:19:368:37Before Machinery[This Is The Glasshouse]867Progressive Rock
2025-09-21 18:10:585:36Streetlight by Streetlight[This Is The Glasshouse]867Progressive Rock
2025-09-21 18:05:210:17Streetlight by Streetlight[This Is The Glasshouse]867Progressive Rock
2025-09-21 15:00:220:02Before Machinery[This Is The Glasshouse]867Progressive Rock
2025-09-21 15:00:125:36Streetlight by Streetlight[This Is The Glasshouse]867Progressive Rock
2025-09-21 02:29:430:24The Diary of Jane[Breaking Benjamin]PhobiaRock
2025-09-21 02:29:201:13Intro[Breaking Benjamin]PhobiaRock
2025-09-21 01:23:282:00The Diary of Jane[Breaking Benjamin]PhobiaRock
2025-09-20 19:51:540:05Chapter X[Sufferer]SuffererPost-Hardcore
2025-09-20 19:51:410:01Chapter IX[Sufferer]SuffererPost-Hardcore
2025-09-20 19:51:390:01Chapter VIII[Sufferer]SuffererPost-Hardcore
2025-09-20 19:51:380:01Chapter VII[Sufferer]SuffererPost-Hardcore
2025-09-20 19:51:360:01Chapter VI[Sufferer]SuffererPost-Hardcore
2025-09-20 19:51:350:01Chapter V[Sufferer]SuffererPost-Hardcore
2025-09-20 19:51:330:01Chapter IV[Sufferer]SuffererPost-Hardcore
2025-09-20 19:51:320:01Chapter III[Sufferer]SuffererPost-Hardcore
2025-09-20 19:51:300:01Chapter II[Sufferer]SuffererPost-Hardcore
2025-09-20 19:51:290:02Chapter I[Sufferer]SuffererPost-Hardcore
+
+ + +

Top Genres

+
+
+ + + + + + + + + + + + + + + + + + +Progressive Rock +Alternative +Post-Rock +Post-Hardcore +Post-Metal +Rock +Shoegaze +Progressive Metal + + +
+
    + +
  1. Progressive Rock: 5:09:49 (64.8%) + +
  2. Alternative: 1:07:16 (14.1%) + +
  3. Post-Rock: 51:41 (10.8%) + +
  4. Post-Hardcore: 37:09 (7.8%) + +
  5. Post-Metal: 4:09 (0.9%) + +
  6. Rock: 3:39 (0.8%) + +
  7. Shoegaze: 2:17 (0.5%) + +
  8. Progressive Metal: 1:47 (0.4%) + +
+
+ + +

Top Albums

+
    +
  1. +
    + + 🖸 Act II: The Meaning of, & All Things Regarding Ms. Leading + 👤 The Dear Hunter + ⏱ 1:25:26 +
    +
  2. + +
  3. +
    + + 🖸 867 + 🫏 This Is The Glasshouse + ⏱ 1:14:56 +
    +
  4. + +
  5. +
    + + 🖸 Twin Fantasy + 👤 Car Seat Headrest + ⏱ 1:07:16 +
    +
  6. + +
  7. +
    + + 🖸 Act III: Life and Death + 👤 The Dear Hunter + ⏱ 57:51 +
    +
  8. + +
  9. +
    + + 🖸 In Keeping Secrets of Silent Earth: 3 + 👤 Coheed and Cambria + ⏱ 51:36 +
    +
  10. + +
  11. +
    + + 🖸 Act I: The Lake South, the River North + 👤 The Dear Hunter + ⏱ 38:38 +
    +
  12. + +
  13. +
    + + 🖸 Sufferer + 👤 Sufferer + ⏱ 37:09 +
    +
  14. + +
  15. +
    + + 🖸 Tales of the Night Forest + 👤 Black Hill & Silent Island + ⏱ 34:13 +
    +
  16. + +
  17. +
    + + 🖸 Lift Your Skinny Fists Like Antennas to Heaven + 👤 Godspeed You! Black Emperor + ⏱ 12:26 +
    +
  18. + +
  19. +
    + + 🖸 A Single Flower + 👤 We Lost the Sea + ⏱ 5:01 +
    +
  20. + +
+ + +

Top Artists

+
    +
  1. + The Dear Hunter: 3:01:57 +
  2. + +
  3. + This Is The Glasshouse: 1:14:56 +
  4. + +
  5. + Car Seat Headrest: 1:07:16 +
  6. + +
  7. + Coheed and Cambria: 52:55 +
  8. + +
  9. + Sufferer: 37:09 +
  10. + +
  11. + Black Hill & Silent Island: 34:13 +
  12. + +
  13. + Godspeed You Black Emperor!: 12:26 +
  14. + +
  15. + We Lost the Sea: 5:01 +
  16. + +
  17. + Breaking Benjamin: 3:39 +
  18. + +
  19. + Harakiri for the Sky: 3:26 +
  20. + +
+ + + + \ No newline at end of file diff --git a/data/albums.json b/data/albums.json new file mode 100644 index 0000000..c5a3d86 --- /dev/null +++ b/data/albums.json @@ -0,0 +1,82 @@ +{ + "albums": [ + { + "name": "Pachinko", + "artist": "Moron Police", + "genre": "Progressive Rock", + "release_group": "e4d30e44-87f9-4818-9d44-d01bb6609c40" + }, + { + "name": "Wake", + "artist": "Hail the Sun", + "genre": "Post-Hardcore", + "release_group": "2ec6fe49-ead9-4c5a-86d5-619af9794b9f" + }, + { + "name": "867", + "artist": "This is the Glasshouse", + "genre": "Progressive Rock", + "release_group": "7787003b-444c-41d1-bfe2-1b0d3041afe7" + }, + { + "name": "wetdream", + "artist": "Willy Rodriguez", + "genre": "Alternative Rock", + "release_group": "be0237d4-1aae-4573-8ccc-8de36cf6de9e" + }, + { + "name": "Collide with the Sky", + "artist": "Pierce the Veil", + "genre": "Post-Hardcore", + "release_group": "fbe216fc-3443-4425-8c99-e538b44e60ac" + }, + { + "name": "Blessed Are the Bonds", + "artist": "The Pax Cecilia", + "genre": "Post-Rock", + "release_group": "bdf7c2e7-0433-3bee-ba8e-d89ca144ef3a" + }, + { + "name": "Lift Your Skinny Fists Like Antennas to Heaven!", + "artist": "Godspeed You! Black Emperor", + "genre": "Post-Rock", + "release_group": "3822abb6-ca53-3ae1-a4ec-7718cb321e9b" + }, + { + "name": "Departure Songs", + "artist": "We Lost the Sea", + "genre": "Post-Rock", + "release_group": "a23ddf04-b1e0-47e5-ae00-f09cbf7cecc5" + }, + { + "name": "Act II: The Meaning of, and All Things Regarding Ms. Leading", + "artist": "The Dear Hunter", + "genre": "Progressive Rock", + "release_group": "c4dec823-cd7a-3967-9119-bee33bc7e5de" + }, + { + "name": "Good Apollo, I'm Burning Star IV, Volume One: From Fear Through the Eyes of Madness", + "artist": "Coheed and Cambria", + "genre": "Progressive Rock", + "release_group": "8fe00242-6d13-3f6a-a9d7-a3fec58ed631" + }, + { + "name": "jiminy", + "artist": "Bear Ghost", + "genre": "Progressive Rock", + "release_group": "42f9e499-42be-4f26-b1f7-ec85a039b729" + }, + { + "name": "Twin Fantasy", + "artist": "Car Seat Headrest", + "genre": "Alternative Rock", + "release_group": "eeab54c6-ad8d-40c9-b595-87bae6a42397" + }, + { + "name": "Above the Earth, Below the Sky", + "artist": "If These Trees Could Talk", + "genre": "Post-Rock", + "release_group": "1c44567c-4015-3063-98db-e62cbd57e7a3" + } + ] +} diff --git a/static/98.css b/static/98.css new file mode 100644 index 0000000..dd99965 --- /dev/null +++ b/static/98.css @@ -0,0 +1,1040 @@ +/*! 98.css v0.1.21 - https://github.com/jdan/98.css */ +/** + * 98.css + * Copyright (c) 2020 Jordan Scales + * https://github.com/jdan/98.css/blob/main/LICENSE + */ + +@font-face { + font-family: unifont; + src: url("/assets/UnifontExMono.woff2"); +} + +:root { + /* Color */ + --text-color: var(--fg); + --surface: var(--bg2); + --button-highlight: var(--bg4); + --button-face: var(--bg3); + --button-shadow: var(--bg-dim); + --window-frame: #0a0a0a; + --dialog-blue: var(--bg-blue); + --dialog-blue-light: var(--blue); + --dialog-gray: #808080; + --dialog-gray-light: #b5b5b5; + --link-blue: var(--blue); + + /* Spacing */ + --element-spacing: 8px; + --grouped-button-spacing: 4px; + --grouped-element-spacing: 6px; + --radio-width: 12px; + --checkbox-width: 13px; + --radio-label-spacing: 6px; + --range-track-height: 4px; + --range-spacing: 10px; + + /* Some detailed computations for radio buttons and checkboxes */ + --radio-total-width-precalc: var(--radio-width) + var(--radio-label-spacing); + --radio-total-width: calc(var(--radio-total-width-precalc)); + --radio-left: calc(-1 * var(--radio-total-width-precalc)); + --radio-dot-width: 4px; + --radio-dot-top: calc(var(--radio-width) / 2 - var(--radio-dot-width) / 2); + --radio-dot-left: calc( + -1 * (var(--radio-total-width-precalc)) + var(--radio-width) / 2 - + var(--radio-dot-width) / 2 + ); + + --checkbox-total-width-precalc: var(--checkbox-width) + + var(--radio-label-spacing); + --checkbox-total-width: calc(var(--checkbox-total-width-precalc)); + --checkbox-left: calc(-1 * var(--checkbox-total-width-precalc)); + --checkmark-width: 7px; + --checkmark-left: 3px; + + /* Borders */ + --border-width: 1px; + --border-raised-outer: + inset -1px -1px var(--window-frame), inset 1px 1px var(--button-highlight); + --border-raised-inner: + inset -2px -2px var(--button-shadow), inset 2px 2px var(--button-face); + --border-sunken-outer: + inset -1px -1px var(--button-highlight), inset 1px 1px var(--window-frame); + --border-sunken-inner: + inset -2px -2px var(--button-face), inset 2px 2px var(--button-shadow); + --default-button-border-raised-outer: + inset -2px -2px var(--window-frame), inset 1px 1px var(--window-frame); + --default-button-border-raised-inner: + inset 2px 2px var(--button-highlight), inset -3px -3px var(--button-shadow), + inset 3px 3px var(--button-face); + --default-button-border-sunken-outer: + inset 2px 2px var(--window-frame), inset -1px -1px var(--window-frame); + --default-button-border-sunken-inner: + inset -2px -2px var(--button-highlight), inset 3px 3px var(--button-shadow), + inset -3px -3px var(--button-face); + + /* Window borders flip button-face and button-highlight */ + --border-window-outer: + inset -1px -1px var(--window-frame), inset 1px 1px var(--button-face); + --border-window-inner: + inset -2px -2px var(--button-shadow), inset 2px 2px var(--button-highlight); + + /* Field borders (checkbox, input, etc) flip window-frame and button-shadow */ + --border-field: + inset -1px -1px var(--button-highlight), inset 1px 1px var(--button-shadow), + inset -2px -2px var(--button-face), inset 2px 2px var(--window-frame); + --border-status-field: + inset -1px -1px var(--button-face), inset 1px 1px var(--button-shadow); + + /* Tabs */ + --border-tab: + inset -1px 0 var(--window-frame), inset 1px 1px var(--button-face), + inset -2px 0 var(--button-shadow), inset 2px 2px var(--button-highlight); +} + +@font-face { + font-family: "Pixelated MS Sans Serif"; + src: url("fonts/converted/ms_sans_serif.woff") format("woff"); + src: url("fonts/converted/ms_sans_serif.woff2") format("woff2"); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: "Pixelated MS Sans Serif"; + src: url("fonts/converted/ms_sans_serif_bold.woff") format("woff"); + src: url("fonts/converted/ms_sans_serif_bold.woff2") format("woff2"); + font-weight: bold; + font-style: normal; +} + +body { + font-family: unifont; + font-size: 1rem; + font-variant-emoji: text; + color: var(--text-color); +} + +button, +label, +input, +legend, +textarea, +select, +option, +table, +ul.tree-view, +.window, +.title-bar, +li[role="tab"] { + font-family: unifont; + -webkit-font-smoothing: none; + font-size: 1rem; +} + +h1 { + font-size: 2rem; +} + +h2 { + font-size: 1rem; +} + +h3 { + font-size: 1rem; +} + +h4 { + font-size: 1rem; +} + +u { + text-decoration: none; + border-bottom: 0.5px solid #222222; +} + +button, +input[type="submit"], +input[type="reset"] { + box-sizing: border-box; + border: none; + color: transparent; + text-shadow: 0 0 var(--text-color); + background: var(--surface); + box-shadow: var(--border-raised-outer), var(--border-raised-inner); + border-radius: 0; + + min-width: 75px; + min-height: 23px; + padding: 0 12px; +} + +button.default, +input[type="submit"].default, +input[type="reset"].default { + box-shadow: + var(--default-button-border-raised-outer), + var(--default-button-border-raised-inner); +} + +.vertical-bar { + width: 4px; + height: 20px; + background: #c0c0c0; + box-shadow: var(--border-raised-outer), var(--border-raised-inner); +} + +button:not(:disabled):active, +input[type="submit"]:not(:disabled):active, +input[type="reset"]:not(:disabled):active { + box-shadow: var(--border-sunken-outer), var(--border-sunken-inner); + text-shadow: 1px 1px var(--text-color); +} + +button.default:not(:disabled):active, +input[type="submit"].default:not(:disabled):active, +input[type="reset"].default:not(:disabled):active { + box-shadow: + var(--default-button-border-sunken-outer), + var(--default-button-border-sunken-inner); +} + +@media (not(hover)) { + button:not(:disabled):hover, + input[type="submit"]:not(:disabled):hover, + input[type="reset"]:not(:disabled):hover { + box-shadow: var(--border-sunken-outer), var(--border-sunken-inner); + } +} + +button:focus, +input[type="submit"]:focus, +input[type="reset"]:focus { + outline: 1px dotted #000000; + outline-offset: -4px; +} + +button::-moz-focus-inner, +input[type="submit"]::-moz-focus-inner, +input[type="reset"]::-moz-focus-inner { + border: 0; +} + +:disabled, +:disabled + label, +input[readonly], +input[readonly] + label { + color: var(--button-shadow); +} + +button:disabled, +input[type="submit"]:disabled, +input[type="reset"]:disabled, +:disabled + label { + text-shadow: 1px 1px 0 var(--button-highlight); +} + +.window { + box-shadow: var(--border-window-outer), var(--border-window-inner); + background: var(--surface); + padding: 3px; +} + +.title-bar { + background: linear-gradient(90deg, var(--bg0), var(--green)); + padding: 3px 2px 3px 3px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.title-bar.inactive { + background: linear-gradient( + 90deg, + var(--dialog-gray), + var(--dialog-gray-light) + ); +} + +.title-bar-text { + color: var(--fg); + letter-spacing: 0; + margin-right: 24px; +} + +.title-bar-controls { + display: flex; +} + +.title-bar-controls button { + padding: 0; + display: block; + min-width: 16px; + min-height: 14px; +} + +.title-bar-controls button:active { + padding: 0; +} + +.title-bar-controls button:focus { + outline: none; +} + +.title-bar-controls button[aria-label="Minimize"], +.title-bar-controls button[aria-label].minimize { + background-image: url("/assets/icon/minimize.svg"); + background-repeat: no-repeat; + background-position: bottom 3px left 4px; +} + +.title-bar-controls button[aria-label="Maximize"], +.title-bar-controls button[aria-label].maximize { + background-image: url("/assets/icon/maximize.svg"); + background-repeat: no-repeat; + background-position: top 2px left 3px; +} + +.title-bar-controls button[aria-label="Maximize"]:disabled, +.title-bar-controls button[aria-label].maximize:disabled { + background-image: url("/assets/icon/maximize-disabled.svg"); + background-repeat: no-repeat; + background-position: top 2px left 3px; +} + +.title-bar-controls button[aria-label="Restore"], +.title-bar-controls button[aria-label].restore { + background-image: url("/assets/icon/restore.svg"); + background-repeat: no-repeat; + background-position: top 2px left 3px; +} + +.title-bar-controls button[aria-label="Help"], +.title-bar-controls button[aria-label].help { + background-image: url("/assets/icon/help.svg"); + background-repeat: no-repeat; + background-position: top 2px left 5px; +} + +.title-bar-controls button[aria-label="Close"], +.title-bar-controls button[aria-label].close { + margin-left: 2px; + background-image: url("/assets/icon/close.svg"); + background-repeat: no-repeat; + background-position: top 3px left 4px; +} + +.status-bar { + margin: 0px 1px; + display: flex; + gap: 1px; +} + +.status-bar-field { + box-shadow: var(--border-status-field); + flex-grow: 1; + padding: 2px 3px; + margin: 0; +} + +.window-body { + margin: var(--element-spacing); +} + +fieldset { + border-image: url("/assets/icon/groupbox-border.svg") 2; + padding: calc(2 * var(--border-width) + var(--element-spacing)); + padding-block-start: var(--element-spacing); + margin: 0; +} + +legend { + background: var(--surface); +} + +.field-row { + display: flex; + align-items: center; +} + +[class^="field-row"] + [class^="field-row"] { + margin-top: var(--grouped-element-spacing); +} + +.field-row > * + * { + margin-left: var(--grouped-element-spacing); +} + +.field-row-stacked { + display: flex; + flex-direction: column; +} + +.field-row-stacked * + * { + margin-top: var(--grouped-element-spacing); +} + +label { + display: inline-flex; + align-items: center; + user-select: none; +} + +input[type="radio"], +input[type="checkbox"] { + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + margin: 0; + background: 0; + position: fixed; + opacity: 0; + border: none; +} + +input[type="radio"] + label, +input[type="checkbox"] + label { + line-height: 13px; +} + +input[type="radio"] + label { + position: relative; + margin-left: var(--radio-total-width); +} + +input[type="radio"] + label::before { + content: ""; + position: absolute; + top: 0; + left: calc(-1 * (var(--radio-total-width-precalc))); + display: inline-block; + width: var(--radio-width); + height: var(--radio-width); + margin-right: var(--radio-label-spacing); + background: url("/assets/icon/radio-border.svg"); +} + +input[type="radio"]:active + label::before { + background: url("/assets/icon/radio-border-disabled.svg"); +} + +input[type="radio"]:checked + label::after { + content: ""; + display: block; + width: var(--radio-dot-width); + height: var(--radio-dot-width); + top: var(--radio-dot-top); + left: var(--radio-dot-left); + position: absolute; + background: url("/assets/icon/radio-dot.svg"); +} + +input[type="radio"]:focus + label, +input[type="checkbox"]:focus + label { + outline: 1px dotted #000000; +} + +input[type="radio"][disabled] + label::before { + background: url("/assets/icon/radio-border-disabled.svg"); +} + +input[type="radio"][disabled]:checked + label::after { + background: url("/assets/icon/radio-dot-disabled.svg"); +} + +input[type="checkbox"] + label { + position: relative; + margin-left: var(--checkbox-total-width); +} + +input[type="checkbox"] + label::before { + content: ""; + position: absolute; + left: calc(-1 * (var(--checkbox-total-width-precalc))); + display: inline-block; + width: var(--checkbox-width); + height: var(--checkbox-width); + background: var(--button-highlight); + box-shadow: var(--border-field); + margin-right: var(--radio-label-spacing); +} + +input[type="checkbox"]:active + label::before { + background: var(--surface); +} + +input[type="checkbox"]:checked + label::after { + content: ""; + display: block; + width: var(--checkmark-width); + height: var(--checkmark-width); + position: absolute; + left: calc( + -1 * (var(--checkbox-total-width-precalc)) + var(--checkmark-left) + ); + background: url("/assets/icon/checkmark.svg"); +} + +input[type="checkbox"][disabled] + label::before { + background: var(--surface); +} + +input[type="checkbox"][disabled]:checked + label::after { + background: url("/assets/icon/checkmark-disabled.svg"); +} + +input[type="text"], +input[type="password"], +input[type="email"], +input[type="url"], +input[type="tel"], +input[type="number"], +input[type="search"], +select, +textarea { + padding: 3px 4px; + border: none; + box-shadow: var(--border-field); + background-color: var(--button-highlight); + box-sizing: border-box; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + border-radius: 0; +} + +input[type="text"], +input[type="password"], +input[type="email"], +input[type="url"], +input[type="tel"], +input[type="search"], +select { + height: 21px; +} +input[type="number"] { + /* need this 1 pixel to fit the spinner controls in box */ + height: 22px; +} +/* clears the ‘X’ from Internet Explorer */ +input[type="search"]::-ms-clear { + display: none; + width: 0; + height: 0; +} +input[type="search"]::-ms-reveal { + display: none; + width: 0; + height: 0; +} +/* clears the ‘X’ from Chrome */ +input[type="search"]::-webkit-search-decoration, +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-results-button, +input[type="search"]::-webkit-search-results-decoration { + display: none; +} + +input[type="text"], +input[type="password"], +input[type="email"], +input[type="url"], +input[type="tel"], +input[type="number"], +input[type="search"] { + /* For some reason descenders are getting cut off without this */ + line-height: 2; +} + +input[type="email"]:disabled, +input[type="url"]:disabled, +input[type="tel"]:disabled, +input[type="password"]:disabled, +input[type="text"]:disabled, +input[type="number"]:disabled, +input[type="search"]:disabled, +input[type="email"]:read-only, +input[type="url"]:read-only, +input[type="tel"]:read-only, +input[type="password"]:read-only, +input[type="text"]:read-only, +input[type="number"]:read-only, +input[type="search"]:read-only, +textarea:disabled { + background-color: var(--surface); +} + +select { + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + position: relative; + padding-right: 32px; + background-image: url("/assets/icon/button-down.svg"); + background-position: top 2px right 2px; + background-repeat: no-repeat; + border-radius: 0; +} + +select:focus, +input[type="text"]:focus, +input[type="password"]:focus, +input[type="email"]:focus, +input[type="url"]:focus, +input[type="tel"]:focus, +input[type="number"]:focus, +input[type="search"]:focus, +textarea:focus { + outline: none; +} + +input[type="range"] { + -webkit-appearance: none; + width: 100%; + background: transparent; +} + +input[type="range"]:focus { + outline: none; +} + +input[type="range"]::-webkit-slider-thumb { + -webkit-appearance: none; + height: 21px; + width: 11px; + background: url("/assets/icon/indicator-horizontal.svg"); + transform: translateY(-8px); + box-shadow: none; + border: none; +} + +input[type="range"].has-box-indicator::-webkit-slider-thumb { + background: url("/assets/icon/indicator-rectangle-horizontal.svg"); + transform: translateY(-10px); +} + +input[type="range"]::-moz-range-thumb { + height: 21px; + width: 11px; + border: 0; + border-radius: 0; + background: url("/assets/icon/indicator-horizontal.svg"); + transform: translateY(2px); +} + +input[type="range"].has-box-indicator::-moz-range-thumb { + background: url("/assets/icon/indicator-rectangle-horizontal.svg"); + transform: translateY(0px); +} + +input[type="range"]::-webkit-slider-runnable-track { + width: 100%; + height: 2px; + box-sizing: border-box; + background: black; + border-right: 1px solid grey; + border-bottom: 1px solid grey; + box-shadow: + 1px 0 0 white, + 1px 1px 0 white, + 0 1px 0 white, + -1px 0 0 darkgrey, + -1px -1px 0 darkgrey, + 0 -1px 0 darkgrey, + -1px 1px 0 white, + 1px -1px darkgrey; +} + +input[type="range"]::-moz-range-track { + width: 100%; + height: 2px; + box-sizing: border-box; + background: black; + border-right: 1px solid grey; + border-bottom: 1px solid grey; + box-shadow: + 1px 0 0 white, + 1px 1px 0 white, + 0 1px 0 white, + -1px 0 0 darkgrey, + -1px -1px 0 darkgrey, + 0 -1px 0 darkgrey, + -1px 1px 0 white, + 1px -1px darkgrey; +} + +.is-vertical { + display: inline-block; + width: 4px; + height: 150px; + transform: translateY(50%); +} + +.is-vertical > input[type="range"] { + width: 150px; + height: 4px; + margin: 0 calc(var(--grouped-element-spacing) + var(--range-spacing)) 0 + var(--range-spacing); + transform-origin: left; + transform: rotate(270deg) translateX(calc(-50% + var(--element-spacing))); +} + +.is-vertical > input[type="range"]::-webkit-slider-runnable-track { + border-left: 1px solid grey; + border-right: 0; + border-bottom: 1px solid grey; + box-shadow: + -1px 0 0 white, + -1px 1px 0 white, + 0 1px 0 white, + 1px 0 0 darkgrey, + 1px -1px 0 darkgrey, + 0 -1px 0 darkgrey, + 1px 1px 0 white, + -1px -1px darkgrey; +} + +.is-vertical > input[type="range"]::-moz-range-track { + border-left: 1px solid grey; + border-right: 0; + border-bottom: 1px solid grey; + box-shadow: + -1px 0 0 white, + -1px 1px 0 white, + 0 1px 0 white, + 1px 0 0 darkgrey, + 1px -1px 0 darkgrey, + 0 -1px 0 darkgrey, + 1px 1px 0 white, + -1px -1px darkgrey; +} + +.is-vertical > input[type="range"]::-webkit-slider-thumb { + transform: translateY(-8px) scaleX(-1); +} + +.is-vertical > input[type="range"].has-box-indicator::-webkit-slider-thumb { + transform: translateY(-10px) scaleX(-1); +} + +.is-vertical > input[type="range"]::-moz-range-thumb { + transform: translateY(2px) scaleX(-1); +} + +.is-vertical > input[type="range"].has-box-indicator::-moz-range-thumb { + transform: translateY(0px) scaleX(-1); +} + +select:focus { + color: var(--button-highlight); + background-color: var(--dialog-blue); +} +select:focus option { + color: #000; + background-color: #fff; +} + +select:active { + background-image: url("/assets/icon/button-down-active.svg"); +} + +a { + color: var(--link-blue); +} + +a:focus { + outline: 1px dotted var(--link-blue); +} + +ul.tree-view { + display: block; + background: var(--button-highlight); + /* box-shadow: var(--border-field); */ + padding: 6px; + margin: 0; +} + +ul.tree-view li { + list-style-type: none; +} + +ul.tree-view a { + text-decoration: none; + color: var(--blue); +} + +ul.tree-view a:focus { + background-color: var(--dialog-blue); + color: var(--button-highlight); +} + +ul.tree-view ul, +ul.tree-view li { + margin-top: 3px; +} + +ul.tree-view ul { + margin-left: 16px; + padding-left: 16px; + /* Goes down too far */ + border-left: 1px dotted #808080; +} + +ul.tree-view ul > li { + position: relative; +} +ul.tree-view ul > li::before { + content: ""; + display: block; + position: absolute; + left: -16px; + top: 6px; + width: 12px; + border-bottom: 1px dotted #808080; +} + +/* Cover the bottom of the left dotted border */ +ul.tree-view ul > li:last-child::after { + content: ""; + display: block; + position: absolute; + left: -20px; + top: 7px; + bottom: 0px; + width: 8px; + background: var(--button-highlight); +} + +ul.tree-view details { + margin-top: 0; +} + +ul.tree-view details[open] summary { + margin-bottom: 0; +} + +ul.tree-view ul details > summary:before { + margin-left: -22px; + position: relative; + z-index: 1; +} + +ul.tree-view details > summary:before { + text-align: center; + display: block; + float: left; + content: "+"; + border: 1px solid var(--bg5); + width: 11px; + height: 11px; + line-height: 10px; + margin-right: 5px; + background-color: var(--bg1); +} + +ul.tree-view details[open] > summary:before { + content: "-"; +} + +ul.tree-view details > summary::marker, +ul.tree-view details > summary::-webkit-details-marker { + content: ""; +} + +pre { + display: block; + background: var(--button-highlight); + box-shadow: var(--border-field); + padding: 12px 8px; + margin: 0; +} + +code, +code * { + font-family: 'unifont'; +} + +summary:focus { + outline: 1px dotted #000000; +} + +::-webkit-scrollbar { + width: 16px; +} +::-webkit-scrollbar:horizontal { + height: 17px; +} + +::-webkit-scrollbar-corner { + background: var(--button-face); +} + +::-webkit-scrollbar-track { + background-image: url("/assets/icon/scrollbar-background.svg"); +} + +::-webkit-scrollbar-thumb { + background-color: var(--button-face); + box-shadow: var(--border-raised-outer), var(--border-raised-inner); +} + +::-webkit-scrollbar-button:horizontal:start:decrement, +::-webkit-scrollbar-button:horizontal:end:increment, +::-webkit-scrollbar-button:vertical:start:decrement, +::-webkit-scrollbar-button:vertical:end:increment { + display: block; +} + +::-webkit-scrollbar-button:vertical:start { + height: 17px; + background-image: url("/assets/icon/button-up.svg"); +} +::-webkit-scrollbar-button:vertical:end { + height: 17px; + background-image: url("/assets/icon/button-down.svg"); +} +::-webkit-scrollbar-button:horizontal:start { + width: 16px; + background-image: url("/assets/icon/button-left.svg"); +} +::-webkit-scrollbar-button:horizontal:end { + width: 16px; + background-image: url("/assets/icon/button-right.svg"); +} + +.window[role="tabpanel"] { + position: relative; + z-index: 2; +} + +menu[role="tablist"] { + position: relative; + margin: 0 0 -2px 0; + text-indent: 0; + list-style-type: none; + display: flex; + padding-left: 3px; +} + +menu[role="tablist"] > li { + border-top-left-radius: 3px; + border-top-right-radius: 3px; + box-shadow: var(--border-tab); + z-index: 1; +} + +menu[role="tablist"] > li[aria-selected="true"] { + padding-bottom: 2px; + margin-top: -2px; + background-color: var(--surface); + position: relative; + z-index: 8; + margin-left: -3px; +} + +menu[role="tablist"] > li > a { + display: block; + color: #222; + margin: 6px; + text-decoration: none; +} +menu[role="tablist"] > li[aria-selected="true"] > a:focus { + outline: none; +} +menu[role="tablist"] > li > a:focus { + outline: 1px dotted #222; +} + +menu[role="tablist"].multirows > li { + flex-grow: 1; + text-align: center; +} +.sunken-panel { + box-sizing: border-box; + border: 2px groove transparent; + border-image: url("/assets/icon/sunken-panel-border.svg") 2; + overflow: auto; + background-color: #fff; +} + +table { + border-collapse: collapse; + position: relative; + text-align: left; + white-space: nowrap; + background-color: var(--bg0); +} + +table > thead > tr > * { + position: sticky; + top: 0; + height: 17px; + box-shadow: var(--border-raised-outer), var(--border-raised-inner); + background: var(--surface); + box-sizing: border-box; + font-weight: normal; + padding: 0 var(--grouped-element-spacing); +} + +table.interactive > tbody > tr { + cursor: pointer; +} + +table > tbody > tr.highlighted { + color: #fff; + background-color: var(--dialog-blue); +} + +table > tbody > tr > * { + padding: 0 var(--grouped-element-spacing); + height: 14px; +} + +.progress-indicator { + height: 32px; + position: relative; + box-shadow: var(--border-sunken-inner); + padding: 4px 4px; + border: none; + box-sizing: border-box; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + border-radius: 0; +} + +.progress-indicator > .progress-indicator-bar { + height: 100%; + display: block; + background-color: var(--dialog-blue); +} + +.progress-indicator.segmented > .progress-indicator-bar { + width: 100%; + background-color: transparent; /* resets the background color which is set to blue in the non-segmented selector */ + background-image: linear-gradient( + 90deg, + var(--dialog-blue) 0 16px, + transparent 0 2px + ); + background-repeat: repeat; + background-size: 18px 100%; +} + +.field-border { + background: var(--button-highlight); + box-shadow: var(--border-field); + padding: 2px; +} + +.field-border-disabled { + background: var(--surface); + box-shadow: var(--border-field); + padding: 2px; +} + +.status-field-border { + background: var(--surface); + box-shadow: var(--border-status-field); + padding: 1px; +} diff --git a/static/assets/UnifontExMono.woff2 b/static/assets/UnifontExMono.woff2 new file mode 100644 index 0000000..e7db71a Binary files /dev/null and b/static/assets/UnifontExMono.woff2 differ diff --git a/static/assets/badges/cc-zero.png b/static/assets/badges/cc-zero.png new file mode 100644 index 0000000..4961cf0 Binary files /dev/null and b/static/assets/badges/cc-zero.png differ diff --git a/static/assets/badges/cookies.png b/static/assets/badges/cookies.png new file mode 100644 index 0000000..70994a7 Binary files /dev/null and b/static/assets/badges/cookies.png differ diff --git a/static/assets/badges/go2hell.gif b/static/assets/badges/go2hell.gif new file mode 100644 index 0000000..7f1290a Binary files /dev/null and b/static/assets/badges/go2hell.gif differ diff --git a/static/assets/badges/indieweb.png b/static/assets/badges/indieweb.png new file mode 100644 index 0000000..a241947 Binary files /dev/null and b/static/assets/badges/indieweb.png differ diff --git a/static/assets/badges/javascript.png b/static/assets/badges/javascript.png new file mode 100644 index 0000000..7ad573f Binary files /dev/null and b/static/assets/badges/javascript.png differ diff --git a/static/assets/badges/lynx.gif b/static/assets/badges/lynx.gif new file mode 100644 index 0000000..452ac5e Binary files /dev/null and b/static/assets/badges/lynx.gif differ diff --git a/static/assets/badges/midi_files_now.gif b/static/assets/badges/midi_files_now.gif new file mode 100644 index 0000000..18a2422 Binary files /dev/null and b/static/assets/badges/midi_files_now.gif differ diff --git a/static/assets/badges/powered-by-debian.gif b/static/assets/badges/powered-by-debian.gif new file mode 100644 index 0000000..1f617c8 Binary files /dev/null and b/static/assets/badges/powered-by-debian.gif differ diff --git a/static/assets/badges/rss.gif b/static/assets/badges/rss.gif new file mode 100644 index 0000000..6d20802 Binary files /dev/null and b/static/assets/badges/rss.gif differ diff --git a/static/assets/badges/soaos.png b/static/assets/badges/soaos.png new file mode 100644 index 0000000..486af90 Binary files /dev/null and b/static/assets/badges/soaos.png differ diff --git a/static/assets/bg.jpg b/static/assets/bg.jpg new file mode 100644 index 0000000..4e9044c Binary files /dev/null and b/static/assets/bg.jpg differ diff --git a/static/assets/construction.gif b/static/assets/construction.gif new file mode 100644 index 0000000..b9c4eeb Binary files /dev/null and b/static/assets/construction.gif differ diff --git a/static/assets/icon/button-down-active.svg b/static/assets/icon/button-down-active.svg new file mode 100644 index 0000000..fa7cac1 --- /dev/null +++ b/static/assets/icon/button-down-active.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/static/assets/icon/button-down.svg b/static/assets/icon/button-down.svg new file mode 100644 index 0000000..7bbaa80 --- /dev/null +++ b/static/assets/icon/button-down.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/static/assets/icon/button-left.svg b/static/assets/icon/button-left.svg new file mode 100644 index 0000000..80421a1 --- /dev/null +++ b/static/assets/icon/button-left.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/static/assets/icon/button-right.svg b/static/assets/icon/button-right.svg new file mode 100644 index 0000000..5573fe0 --- /dev/null +++ b/static/assets/icon/button-right.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/static/assets/icon/button-up.svg b/static/assets/icon/button-up.svg new file mode 100644 index 0000000..4b25846 --- /dev/null +++ b/static/assets/icon/button-up.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/static/assets/icon/checkmark-disabled.svg b/static/assets/icon/checkmark-disabled.svg new file mode 100644 index 0000000..f9d34b9 --- /dev/null +++ b/static/assets/icon/checkmark-disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/assets/icon/checkmark.svg b/static/assets/icon/checkmark.svg new file mode 100644 index 0000000..297996a --- /dev/null +++ b/static/assets/icon/checkmark.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/assets/icon/close.svg b/static/assets/icon/close.svg new file mode 100644 index 0000000..9b426f5 --- /dev/null +++ b/static/assets/icon/close.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/assets/icon/groupbox-border.svg b/static/assets/icon/groupbox-border.svg new file mode 100644 index 0000000..a205da9 --- /dev/null +++ b/static/assets/icon/groupbox-border.svg @@ -0,0 +1,4 @@ + + + + diff --git a/static/assets/icon/help.svg b/static/assets/icon/help.svg new file mode 100644 index 0000000..f7a5c07 --- /dev/null +++ b/static/assets/icon/help.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/static/assets/icon/indicator-horizontal.svg b/static/assets/icon/indicator-horizontal.svg new file mode 100644 index 0000000..026baf5 --- /dev/null +++ b/static/assets/icon/indicator-horizontal.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/static/assets/icon/indicator-rectangle-horizontal.svg b/static/assets/icon/indicator-rectangle-horizontal.svg new file mode 100644 index 0000000..9bbfb58 --- /dev/null +++ b/static/assets/icon/indicator-rectangle-horizontal.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/static/assets/icon/maximize-disabled.svg b/static/assets/icon/maximize-disabled.svg new file mode 100644 index 0000000..34c7ff9 --- /dev/null +++ b/static/assets/icon/maximize-disabled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/static/assets/icon/maximize.svg b/static/assets/icon/maximize.svg new file mode 100644 index 0000000..19f8b09 --- /dev/null +++ b/static/assets/icon/maximize.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/assets/icon/minimize.svg b/static/assets/icon/minimize.svg new file mode 100644 index 0000000..6231e4c --- /dev/null +++ b/static/assets/icon/minimize.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/assets/icon/radio-border-disabled.svg b/static/assets/icon/radio-border-disabled.svg new file mode 100644 index 0000000..c53fa15 --- /dev/null +++ b/static/assets/icon/radio-border-disabled.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/static/assets/icon/radio-border.svg b/static/assets/icon/radio-border.svg new file mode 100644 index 0000000..e70978e --- /dev/null +++ b/static/assets/icon/radio-border.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/static/assets/icon/radio-dot-disabled.svg b/static/assets/icon/radio-dot-disabled.svg new file mode 100644 index 0000000..e1a76fc --- /dev/null +++ b/static/assets/icon/radio-dot-disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/assets/icon/radio-dot.svg b/static/assets/icon/radio-dot.svg new file mode 100644 index 0000000..f19e1b8 --- /dev/null +++ b/static/assets/icon/radio-dot.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/assets/icon/restore.svg b/static/assets/icon/restore.svg new file mode 100644 index 0000000..bd8fe15 --- /dev/null +++ b/static/assets/icon/restore.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/static/assets/icon/scrollbar-background.svg b/static/assets/icon/scrollbar-background.svg new file mode 100644 index 0000000..04b2f59 --- /dev/null +++ b/static/assets/icon/scrollbar-background.svg @@ -0,0 +1,4 @@ + + + + diff --git a/static/assets/icon/sunken-panel-border.svg b/static/assets/icon/sunken-panel-border.svg new file mode 100644 index 0000000..c13e8ce --- /dev/null +++ b/static/assets/icon/sunken-panel-border.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/static/assets/music/867.png b/static/assets/music/867.png new file mode 100644 index 0000000..d416100 Binary files /dev/null and b/static/assets/music/867.png differ diff --git a/static/assets/music/act_ii.jpg b/static/assets/music/act_ii.jpg new file mode 100644 index 0000000..97b2e82 Binary files /dev/null and b/static/assets/music/act_ii.jpg differ diff --git a/static/assets/music/apollo.jpg b/static/assets/music/apollo.jpg new file mode 100644 index 0000000..9c742ee Binary files /dev/null and b/static/assets/music/apollo.jpg differ diff --git a/static/assets/music/atebts.jpg b/static/assets/music/atebts.jpg new file mode 100644 index 0000000..ca68692 Binary files /dev/null and b/static/assets/music/atebts.jpg differ diff --git a/static/assets/music/batb.jpg b/static/assets/music/batb.jpg new file mode 100644 index 0000000..a6a81ec Binary files /dev/null and b/static/assets/music/batb.jpg differ diff --git a/static/assets/music/cwts.jpg b/static/assets/music/cwts.jpg new file mode 100644 index 0000000..0cca425 Binary files /dev/null and b/static/assets/music/cwts.jpg differ diff --git a/static/assets/music/departure_songs.jpg b/static/assets/music/departure_songs.jpg new file mode 100644 index 0000000..2699d5d Binary files /dev/null and b/static/assets/music/departure_songs.jpg differ diff --git a/static/assets/music/jiminy.jpg b/static/assets/music/jiminy.jpg new file mode 100644 index 0000000..a216de4 Binary files /dev/null and b/static/assets/music/jiminy.jpg differ diff --git a/static/assets/music/lysf.jpg b/static/assets/music/lysf.jpg new file mode 100644 index 0000000..8605559 Binary files /dev/null and b/static/assets/music/lysf.jpg differ diff --git a/static/assets/music/twin_fantasy.jpg b/static/assets/music/twin_fantasy.jpg new file mode 100644 index 0000000..f83e135 Binary files /dev/null and b/static/assets/music/twin_fantasy.jpg differ diff --git a/static/assets/music/wake.jpg b/static/assets/music/wake.jpg new file mode 100644 index 0000000..d51184d Binary files /dev/null and b/static/assets/music/wake.jpg differ diff --git a/static/assets/music/wetdream.png b/static/assets/music/wetdream.png new file mode 100644 index 0000000..862544c Binary files /dev/null and b/static/assets/music/wetdream.png differ diff --git a/static/assets/wrttyh.flac b/static/assets/wrttyh.flac new file mode 100644 index 0000000..c51b54c Binary files /dev/null and b/static/assets/wrttyh.flac differ diff --git a/static/favicon.png b/static/favicon.png new file mode 100644 index 0000000..fac014b Binary files /dev/null and b/static/favicon.png differ diff --git a/static/hell.css b/static/hell.css new file mode 100644 index 0000000..572f3d1 --- /dev/null +++ b/static/hell.css @@ -0,0 +1,102 @@ +html { + background: url("/hell/bg.jpg"); + background-attachment: fixed; + background-size: cover; + image-rendering: pixelated; +} + +body { + margin: auto 0; + max-width: unset; + margin-right: auto; +} + +@keyframes fire { + 0% { + text-shadow: + 0 0 20px #fefcc9, + 10px -10px 30px #feec85, + -20px -20px 40px #ffae34, + 20px -40px 50px #ec760c, + -20px -60px 60px #cd4606, + 0 -80px 70px #973716, + 10px -90px 80px #451b0e; + } + 100% { + text-shadow: + 0 0 20px #fefcc9, + 10px -10px 30px #fefcc9, + -20px -20px 40px #feec85, + 22px -42px 60px #ffae34, + -22px -58px 50px #ec760c, + 0 -82px 80px #cd4606, + 10px -90px 80px #973716; + } +} + +.hellfire { + color: var(--orange); + animation: fire 1s ease-in-out infinite alternate; +} + +massive-fucking-background-flame { + width: 100%; + height: 100%; + position: fixed; + top: 0; + left: 0; + z-index: -999; + background-image: url("/hell/smallfire.gif"); + background-size: auto 128px; + opacity: 0.25; + --pan: 0 -128px; + animation: pan linear 3s infinite; +} + +a { + color: black; +} + +#inverted-cross { + display: inline-block; + rotate: 180deg; + transition: + rotate 2s, + color 0.5s; + --glow-color: red; + animation: glow 4s linear infinite; +} + +#inverted-cross:hover { + rotate: 0deg; + --glow-color: white; + color: var(--yellow); + transition: + rotate 2s, + color 1s; +} + +.evil { + color: black; +} + +.huge { + font-size: 8rem; +} + +.struck { + opacity: 0.5; + position: absolute; + left: 0; + z-index: -999; + user-select: none; +} + +@keyframes pan { + from { + background-position: 0 0; + } + to { + background-position: var(--pan); + } +} diff --git a/static/index.css b/static/index.css new file mode 100644 index 0000000..9ae6d1e --- /dev/null +++ b/static/index.css @@ -0,0 +1,27 @@ +.section { + background-color: var(--bg1); +} + +html { + background: url("/assets/bg.jpg"); + background-attachment: fixed; + background-size: cover; + image-rendering: pixelated; +} + +#weird-fucking-header-container { + display: inline-grid; + grid-template-columns: 1fr; + grid-template-rows: 1fr; +} + +.whatever { + grid-area: 1 / 1 / 2 / 2; +} + +#badge-grid { + display: inline-grid; + grid-template-columns: 1fr 1fr 1fr; + gap: 4px; +} + diff --git a/static/index.js b/static/index.js new file mode 100644 index 0000000..e69de29 diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..804ce8d --- /dev/null +++ b/static/style.css @@ -0,0 +1,225 @@ +:root { + --red: #e67e80; + --orange: #e69875; + --yellow: #dbbc7f; + --green: #a7c080; + --blue: #7fbbb3; + --aqua: #83c092; + --purple: #d699b6; + --fg: #d3c6aa; + --statusline1: #a7c080; + --statusline2: #d3c6aa; + --statusline3: #e67e80; + --gray0: #7a8478; + --gray1: #859289; + --gray2: #9da9a0; + + --bg-dim: #232a2e; + --bg0: #2d353b; + --bg1: #343f44; + --bg2: #3d484d; + --bg3: #475258; + --bg4: #4f585e; + --bg5: #56635f; + --bg-red: #4c3743; + --bg-visual: #493b40; + --bg-yellow: #45443c; + --bg-green: #3c4841; + --bg-blue: #384b55; +} + +html { + background: url("/assets/bg.jpg"); + background-attachment: fixed; + background-size: cover; + image-rendering: pixelated; +} + +a:not(:has(img, div))[href$=".vsix"]::after { + content: " ⮋"; +} + +a:not(:has(img, div))[target="_blank"]::after { + content: " ⎘"; +} + +a:not(:has(img,div))[href^="https://tv.soaos.dev/w/"]::before +{ + content: "📺 "; +} + +ul { + list-style-type: "• "; +} + +pre { + overflow: auto; +} + +@keyframes blinker { + 50% { + opacity: 0; + } +} + +.under-construction { + color: var(--yellow); + animation: blinker 1s linear infinite; +} + +@keyframes glow { + 50% { + text-shadow: + 0 0 0px var(--glow-color), + 0 0 20px hsl(from var(--glow-color) h s calc(l - 10)); + } +} + +@keyframes glow-box { + 50% { + box-shadow: + 0 0 0px var(--glow-color), + 0 0 20px hsl(from var(--glow-color) h s calc(l - 10)); + } +} + +.evil { + color: var(--red); + --glow-color: red; + animation: glow 4s linear infinite; +} + +.evil-box { + color: var(--red); + --glow-color: red; + animation: glow-box 4s linear infinite; +} + +.holy { + color: var(--yellow); + --glow-color: white; + animation: glow 4s linear infinite; +} + +a.evil:visited { + color: var(--red); +} + +a.evil:hover { + color: red; +} + +.hidden { + display: none; +} + +.no-bullets { + list-style-type: none; + padding-left: 0; + :w; +} + +#badge-grid { + display: flex; + gap: 4px; + flex-wrap: wrap; +} + +.unselectable { + user-select: none; +} + +.window { + margin: 1rem; +} + +.red-on-white { + color: red; + background-color: white; +} + +.centered { + display: flex; + justify-content: center; +} + +body { + margin: auto 0; + max-width: 800px; + margin-right: auto; +} + +[data-tooltip]:hover::after { + content: attr(data-tooltip); + border: 1px solid var(--bg-dim); + background: var(--fg); + color: var(--bg-dim); + display: inline-block; + white-space: pre-wrap; + text-align: left; + padding: 0.3em; + position: absolute; + z-index: 98; +} + +.flip { + position: relative; +} + +.hidden-selectable { + opacity: 0; +} + +.flip::after { + display: inline-block; + position: absolute; + content: attr(data-title); + transform: rotateY(180deg); + clip-path: polygon(0 0, 50.1% 0, 50.1% 100%, 0 100%); + visibility: visible; + left: 0; +} + +.flip::before { + display: inline-block; + position: absolute; + content: attr(data-title); + clip-path: polygon(0 0, 50.1% 0, 50.1% 100%, 0 100%); + visibility: visible; +} + +#statuscafe { +padding: .5em; +background-color: var(--button-highlight); +} +#statuscafe-username { +margin-bottom: .5em; +} +#statuscafe-content { +margin: 0 1em 0.5em 1em; +} + +.album-thumb { + image-rendering: auto; + width: 12rem; +} + +/* h2 { */ +/* color: var(--orange); */ +/* } */ +/**/ +/* h3 { */ +/* color: var(--yellow); */ +/* } */ +/**/ +/* h4 { */ +/* color: var(--green); */ +/* } */ +/**/ +/* h5 { */ +/* color: var(--blue); */ +/* } */ +/**/ +/* h6 { */ +/* color: var(--purple); */ +/* } */ diff --git a/static/style.css.bak b/static/style.css.bak new file mode 100644 index 0000000..22815a7 --- /dev/null +++ b/static/style.css.bak @@ -0,0 +1,224 @@ +@font-face { + font-family: unifont; + src: url("/assets/UnifontExMono.woff2"); +} + +:root { + --red: #e67e80; + --orange: #e69875; + --yellow: #dbbc7f; + --green: #a7c080; + --blue: #7fbbb3; + --aqua: #83c092; + --purple: #d699b6; + --fg: #d3c6aa; + --statusline1: #a7c080; + --statusline2: #d3c6aa; + --statusline3: #e67e80; + --gray0: #7a8478; + --gray1: #859289; + --gray2: #9da9a0; + + --bg-dim: #232a2e; + --bg0: #2d353b; + --bg1: #343f44; + --bg2: #3d484d; + --bg3: #475258; + --bg4: #4f585e; + --bg5: #56635f; + --bg-red: #4c3743; + --bg-visual: #493b40; + --bg-yellow: #45443c; + --bg-green: #3c4841; + --bg-blue: #384b55; +} + +body { + font-family: unifont; + font-size: 16px; + color: var(--fg); + perspective-origin: top left; +} + +pre, +code, +samp { + font-family: unifont; +} + +samp { + color: var(--gray2); +} + +.section { + margin: 1rem; + padding: 1rem; +} + +.horizontal-container { + display: flex; + justify-content: space-between; +} + +a { + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-weight: normal; + text-decoration: underline; +} + +h1 { + font-size: 48px; +} + +h2 { + font-size: 32px; +} + +h3 { + font-size: 16px; +} + +.centered { + text-align: center; +} + +.center-content { + display: flex; + justify-content: center; +} + +.half { + clip-path: polygon(0 0, 50.1% 0, 50.1% 100%, 0 100%); +} + +.flip-x { + transform: scaleX(-1); +} + +.unselectable { + user-select: none; +} + +@keyframes blinker { + 50% { + opacity: 0; + } +} + +.under-construction { + color: var(--yellow); + animation: blinker 1s linear infinite; +} + +@keyframes glow { + 50% { + text-shadow: + 0 0 10px var(--glow-color), + 0 0 20px var(--glow-color), + 0 0 40px var(--glow-color), + 0 0 80px hsl(from var(--glow-color) h s calc(l - 10)), + 0 0 120px hsl(from var(--glow-color) h s calc(l - 10)); + } +} + +.evil { + color: var(--red); + --glow-color: red; + animation: glow 4s linear infinite; +} + +.holy { + color: var(--yellow); + --glow-color: white; + animation: glow 4s linear infinite; +} + +a.evil:visited { + color: var(--red); +} + +a.evil:hover { + color: red; +} + +.inline-block { + display: inline-block; +} + +.no-bullets { + list-style-type: none; + padding-left: 0; +} + +.red-on-white { + color: red; + background-color: white; +} + +.hidden { + display: none; +} + +.huge { + font-size: 128px; +} + +.struck { + opacity: 0.5; + position: absolute; + z-index: -999; + user-select: none; +} + +@keyframes pan { + from { + background-position: 0 0; + } + to { + background-position: var(--pan); + } +} + +.bold { + font-weight: bold; +} + +.column { + display: flex; + flex-direction: column; + align-items: center; +} + +/* Links */ +a { + color: var(--blue); +} + +a:visited { + color: var(--purple); +} + +a:not(:has(img, div))[target="_blank"]::after { + content: " ⎘"; +} + +a:not(:has(img,div))[href^="https://tv.soaos.dev/w/"]::before +{ + content: "📺 "; +} + +ul { + list-style-type: "• "; +} diff --git a/templates/about.html b/templates/about.html new file mode 100644 index 0000000..613c944 --- /dev/null +++ b/templates/about.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} +{% import "macros.html" as macros %} +{% block content %} + +{% set soaos = macros::soaosed(text="soaos") | trim %} +{{ macros::window(title="🦌 About Me", close="..") }} +{{ page.content | safe }} +{{ macros::endwindow() }} + +{% endblock content %} \ No newline at end of file diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..2902d19 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,115 @@ +{% import "macros.html" as macros %} + + + + + soaos + + + + + + {% block headcontent %} {% endblock headcontent %} + + +
+
+
+
𐂂 Badass header
+
+
+

+ 𐂂🌲🌲🌲 soaos 🌲🌲🌲𐂂 +

+
+
+
+
{% block content %} {% endblock content %}
+
+
+
🎖 Badges
+
+
+
    +
  • + Lynx Compatible +
  • +
  • + Powered by Debian +
  • +
  • + Go 2 Hell Now! +
  • +
  • + soaos.dev +
  • +
  • + Cookie-Free Page +
  • +
  • + Work on this site released under CC0 +
  • +
  • + Work on this site released under CC0 +
  • +
  • + RSS Feed +
  • +
  • + Status Cafe +
  • +
+
+
+

+ █🍁█ +

+

+ + EVILRING + +

+

+ {{ now() }} +

+

+ soaos +

+
+
+ + diff --git a/templates/blog.html b/templates/blog.html new file mode 100644 index 0000000..e454299 --- /dev/null +++ b/templates/blog.html @@ -0,0 +1,37 @@ +{% extends "base.html" %} +{% block content %} +
+
+
📖 Blog
+
+ +
+
+
+

+ Welcome to my blog! This is where I'll post longer content about stuff I'm + working on. I'm working out some channels for posting day-to-day short + form shit too so keep an eye out for that. +

+

Latest Posts

+
+
    + {% for year, posts in section.pages | group_by(attribute="year") %} +
  • +
    + {{ year }} +
      + {% for post in posts %} +
    • + {{post.title}} - {{post.date}} +
    • + {% endfor %} +
    +
    +
  • + {% endfor %} +
+
+
+
+{% endblock content %} \ No newline at end of file diff --git a/templates/heaven.html b/templates/heaven.html new file mode 100644 index 0000000..93a86ac --- /dev/null +++ b/templates/heaven.html @@ -0,0 +1,29 @@ + + + + + + heaven + + + + + + +
+
+
+ 🕊🕊🕊 + (please enable autoplay to hear music lol) + +
+

😇 GOD BLESS YOU FRIEND... YOU ARE IN HEAVEN! 😇

+
+ YOU ARE LOVED 💖🌹 +
+ + + + diff --git a/templates/hell.html b/templates/hell.html new file mode 100644 index 0000000..d799db3 --- /dev/null +++ b/templates/hell.html @@ -0,0 +1,22 @@ + + + + + hell + + + + + + + + + + {{ section.content | safe }} + + diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..baee426 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,60 @@ +{% extends "base.html" %} +{% import "macros.html" as macros %} +{% block content %} + +{% set soaos = macros::soaosed(text="soaos") | trim %} +{% set Soaos = macros::soaosed(text="Soaos") | trim %} +{{macros::window(title="👤 Welcome to " ~ soaos ~ ".dev")}} +
+
+
+
+
+{{ macros::treeview(height="16rem")}} + +{{ macros::subtree(name="On this site", open=true) }} +{{ macros::treelink(text="📖 Blog", url="/blog") }} + +{{ macros::treelink(text="🦌 About Me", url="/about", wip=true, wip_tooltip="🦌 -honestly these things are redundant, +everything is under construction") }} +{{ macros::endsubtree() }} + +{{ macros::subtree(name=Soaos ~ ' Services', open=true) }} +{{ macros::treelink(text="🌱 Git Repositories", url="https://git.soaos.dev", blank=true, wip=true) }} +{{ macros::treelink(text="📺 Videos", url="https://tv.soaos.dev/c/soaosdev", blank=true) }} + + + +{{ macros::endsubtree() }} + +{{ macros::subtree(name="Find me on the Web", open=true) }} +{{ macros::treelink(text="📧 E-Mail: soaos@soaos.dev", url="mailto:soaos@soaos.dev", rel="me") }} +{{ macros::treelink(text="💬 Matrix: @soaos:matrix.soaos.dev", url="https://matrix.to/#/@soaos:matrix.soaos.dev", +rel="me") }} +{{ macros::treelink(text="☕ StatusCafe: soaos", url="https://status.cafe/users/soaos", blank=true, rel="me") }} +{{ macros::treelink(text="🐘 Mastodon: soaos@furry.engineer", url="http://soaos.dog", rel="me", blank=true) }} + + +{{ macros::endsubtree() }} + +{{macros::endtreeview()}} +{{macros::endwindow()}} + + +{% endblock content %} \ No newline at end of file diff --git a/templates/macros.html b/templates/macros.html new file mode 100644 index 0000000..5642fc2 --- /dev/null +++ b/templates/macros.html @@ -0,0 +1,62 @@ +{% macro window(title, close=false) %} +
+
+
{{title | safe}}
+
+ {% if close %} + + {% endif %} +
+
+
+{% endmacro window %} + +{% macro endwindow() %} +
+
+{% endmacro endwindow %} + + +{% macro soaosed(text) %} +{{ text }} +{% endmacro soaosed %} + +{% macro treeview(height="auto") %} +
+
    +{% endmacro treeview %} + +{% macro endtreeview() %} +
+
+{% endmacro endtreeview %} + +{% macro subtree(name, url="", open=false, blank=false) %} +
  • +
    + + {% if url %}{% else %}{% endif %} + {{ name | safe }} + {% if url %}{% else %}{% endif %} + +
      +{% endmacro subtree %} + +{% macro endsubtree() %} +
    +
    +
  • +{% endmacro endsubtree %} + +{% macro treelink(text, url="", blank=false, wip=false, wip_tooltip="") %} +
  • + {{ text | safe }} + {% if wip %} + + + + {% endif %} +
  • +{% endmacro treelink %} \ No newline at end of file diff --git a/templates/page.html b/templates/page.html new file mode 100644 index 0000000..4cd271b --- /dev/null +++ b/templates/page.html @@ -0,0 +1,2 @@ +{% extends "base.html" %} {% block content %} {{ page.content | safe }} {% +endblock content %} diff --git a/templates/post.html b/templates/post.html new file mode 100644 index 0000000..ae884d5 --- /dev/null +++ b/templates/post.html @@ -0,0 +1,30 @@ +{% extends "base.html" %} +{% block headcontent %} + + + + + + + + + +{% endblock headcontent %} +{% block content %} +
    +
    + 📰 Post: {{ page.title }} +
    + + +
    +
    +
    +

    {{ page.title }}

    +
    {{ page.content | safe}}
    +
    +
    +{% endblock content %} diff --git a/templates/section.html b/templates/section.html new file mode 100644 index 0000000..0bf66d0 --- /dev/null +++ b/templates/section.html @@ -0,0 +1,2 @@ +{% extends "base.html" %} {% block content %} {{ section.content | safe }} {% +endblock content %} diff --git a/templates/shortcodes/age.html b/templates/shortcodes/age.html new file mode 100644 index 0000000..8af71ec --- /dev/null +++ b/templates/shortcodes/age.html @@ -0,0 +1 @@ +{{ now() | date(format='%Y') | int - 2002 }} \ No newline at end of file diff --git a/templates/shortcodes/album_list.html b/templates/shortcodes/album_list.html new file mode 100644 index 0000000..74439ca --- /dev/null +++ b/templates/shortcodes/album_list.html @@ -0,0 +1,16 @@ +{% import "macros.html" as macros %} +{% set data = load_data(path="data/albums.json") %} + +{{ macros::treeview(height="32rem") }} +{% for genre, albums in data.albums | group_by(attribute="genre") %} + +{{ macros::subtree(name=genre, open=false) }} +{% for album in albums %} + {{ macros::treelink(text=album.artist ~ " - " ~ album.name ~ `
    + `, + url="https://musicbrainz.org/release-group/" ~ album.release_group, blank=true) }} +{% endfor %} +{{ macros::endsubtree() }} + +{% endfor %} +{{ macros::endtreeview() }} diff --git a/templates/shortcodes/soaosed.html b/templates/shortcodes/soaosed.html new file mode 100644 index 0000000..c5ae639 --- /dev/null +++ b/templates/shortcodes/soaosed.html @@ -0,0 +1,3 @@ +{{ body }} \ No newline at end of file diff --git a/templates/shortcodes/subtree.html b/templates/shortcodes/subtree.html new file mode 100644 index 0000000..09f29ce --- /dev/null +++ b/templates/shortcodes/subtree.html @@ -0,0 +1,12 @@ +
  • +
    + + {% if url %}{% else %}{% endif %} + {{ name | safe }} + {% if url %}{% else %}{% endif %} + +
      + {{ body | markdown | safe }} +
    +
    +
  • \ No newline at end of file diff --git a/templates/shortcodes/title_bar.html b/templates/shortcodes/title_bar.html new file mode 100644 index 0000000..facc6fb --- /dev/null +++ b/templates/shortcodes/title_bar.html @@ -0,0 +1,8 @@ +
    +
    {{ body | safe }}
    +
    + {% if close %} + + {% endif %} +
    +
    diff --git a/templates/shortcodes/tree_view.html b/templates/shortcodes/tree_view.html new file mode 100644 index 0000000..e8de782 --- /dev/null +++ b/templates/shortcodes/tree_view.html @@ -0,0 +1,5 @@ +
    +
      + {{ body | markdown | safe }} +
    +
    diff --git a/templates/shortcodes/treelink.html b/templates/shortcodes/treelink.html new file mode 100644 index 0000000..ff80fdc --- /dev/null +++ b/templates/shortcodes/treelink.html @@ -0,0 +1,8 @@ +
  • + {{ text }} + {% if wip %} + + + + {% endif %} +
  • \ No newline at end of file diff --git a/templates/shortcodes/window.html b/templates/shortcodes/window.html new file mode 100644 index 0000000..8abddbe --- /dev/null +++ b/templates/shortcodes/window.html @@ -0,0 +1,3 @@ +
    +{{ body | safe }} +
    diff --git a/templates/shortcodes/window_body.html b/templates/shortcodes/window_body.html new file mode 100644 index 0000000..f7c84a7 --- /dev/null +++ b/templates/shortcodes/window_body.html @@ -0,0 +1 @@ +
    {{ body | markdown | safe }}
    -- cgit v1.3-2-g0d8e