My personal website, in gleam+lustre!
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Ported: Nix, Mise and other spice!


Signed-off-by: MLC Bloeiman <mar@strawmelonjuice.com>

+209 -111
+6 -13
dev/homepage/prepare.gleam
··· 3 3 4 4 import gleam/erlang/application 5 5 import gleam/io 6 - import homepage 7 6 import gleam/json 8 7 import gleam/list 9 8 import gleam/result 10 9 import gleam/string 10 + import homepage 11 11 import simplifile 12 12 13 13 @external(javascript, "./nonexistent.js", "okay") ··· 46 46 } 47 47 48 48 fn generate_sitemap() { 49 - let contents = "<?xml version=\"1.0\" encoding=\"UTF-8\"?> 49 + let contents = "<?xml version=\"1.0\" encoding=\"UTF-8\"?> 50 50 51 51 <urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"> 52 - " <> 53 - 54 - list.map(homepage.sitemap(), fn(entry) { 55 - "<url> 52 + " <> list.map(homepage.sitemap(), fn(entry) { "<url> 56 53 57 54 <loc>https://strawmelonjuice.com" <> entry.route |> homepage.to_url <> "</loc> 58 55 ··· 62 59 63 60 <priority>0.8</priority> 64 61 65 - </url>" 66 - 67 - })|> string.join("\n")<>"</urlset>" 62 + </url>" }) 63 + |> string.join("\n") <> "</urlset>" 68 64 69 65 use _ <- result.try( 70 - simplifile.write( 71 - "./assets/sitemap.xml", 72 - contents:, 73 - ) 66 + simplifile.write("./assets/sitemap.xml", contents:) 74 67 |> result.replace_error( 75 68 "❌\tSomething went wrong writing `./assets/sitemap.xml`.", 76 69 ),
+130 -98
src/homepage.gleam
··· 6 6 fn posts() { 7 7 [ 8 8 Post( 9 + id: 900, 10 + title: "New site, who dis?", 11 + summary: "I rewrote this site from scratch!", 12 + published: Date(2026, March, 11), 13 + revised: None, 14 + body: File(Djot, "./written-contents/blog/personal/2026/new-site.dj"), 15 + aliases: ["first"], 16 + comments: MastodonStatusLink("pony.social", "116213072855558563"), 17 + category: "Site meta", 18 + tags: [ 19 + "gleam", 20 + "lustre", 21 + "djot", 22 + "blogging", 23 + "website development", 24 + "web development", 25 + "programming", 26 + "coding", 27 + "software engineering", 28 + "technology", 29 + "meta", 30 + "personal blog", 31 + ".blog-personal", 32 + ], 33 + ), 34 + Post( 35 + id: 899, 36 + category: "Personal", 37 + title: "Nix, Mise and other spice.", 38 + summary: "I'm Just making some changes!", 39 + published: Date(2026, February, 7), 40 + revised: None, 41 + body: File(Djot, "./written-contents/blog/personal/2026/nix.dj"), 42 + tags: [ 43 + "Technology", 44 + "Programming", 45 + "Mise-en-place", 46 + "NixOS", 47 + "dotfiles", 48 + "Software development", 49 + "Development tooling", 50 + "Distrohopping", 51 + "Technology Ethics", 52 + ".blog-personal", 53 + ], 54 + aliases: ["nix-chezmoi-mise-just"], 55 + comments: MastodonStatusLink("pony.social", "116241293057774064"), 56 + ), 57 + Post( 9 58 id: 1, 10 59 title: "Amigo & Ferry say hi!", 11 60 summary: "Introducing Amigo The Slug and Ferry the cactus - characters created for the Blub discord server that became fan favorites!", ··· 36 85 ".creative-art", 37 86 ], 38 87 ), 39 - Post( 40 - id: 900, 41 - title: "New site, who dis?", 42 - summary: "I rewrote this site from scratch!", 43 - published: Date(2026, March, 11), 44 - revised: None, 45 - body: File(Djot, "./written-contents/blog/personal/2026/new-site.dj"), 46 - aliases: ["first"], 47 - comments: MastodonStatusLink("pony.social", "116213072855558563"), 48 - category: "Site meta", 49 - tags: [ 50 - "gleam", 51 - "lustre", 52 - "djot", 53 - "blogging", 54 - "website development", 55 - "web development", 56 - "programming", 57 - "coding", 58 - "software engineering", 59 - "technology", 60 - "meta", 61 - "personal blog", 62 - ".blog-personal", 63 - ], 64 - ), 65 88 ] 66 89 } 67 90 68 - 69 - 70 91 const pages = [ 71 92 // If index isn't fist, things break. 72 93 Entry(Index, "Homepage", Date(2026, March, 16), NotFound(uri.empty)), 73 - Entry( Me, "About me", Date(2026, March, 16), Index), 94 + Entry(Me, "About me", Date(2026, March, 16), Index), 74 95 Entry(Portfolio, "Portfolio", Date(2026, March, 16), Me), 75 - Entry( Links, "Links", Date(2026, March, 16), Me), 96 + Entry(Links, "Links", Date(2026, March, 16), Me), 76 97 Entry(Posts, "Posts", Date(2026, March, 16), Index), 77 98 Entry(Sitemap, "Sitemap", Date(2026, March, 16), Index), 78 99 Entry(AllAndEverything, "/everything page", Date(2026, March, 16), Index), ··· 89 110 Some(new) -> new 90 111 None -> post.published 91 112 }, 92 - parent: Posts 113 + parent: Posts, 93 114 ) 94 115 }) 95 116 pages |> list.append(posts) ··· 99 120 let assert Ok(index_entry) = list.first(pages) 100 121 // To be clear: 101 122 let assert Index = index_entry.route 102 - [ 103 - title("Sitemap"), html.br([attribute.class("mt-4")]) 123 + [ 124 + title("Sitemap"), 125 + html.br([attribute.class("mt-4")]), 104 126 105 - ,html.ul([attribute.class("list-['=>'] list-inside")],[render_sitemap_recursively(start_entry: index_entry, level: 0)]) 106 - 107 - ] 127 + html.ul([attribute.class("list-['=>'] list-inside")], [ 128 + render_sitemap_recursively(start_entry: index_entry, level: 0), 129 + ]), 130 + ] 108 131 } 132 + 109 133 // For now we render a boring with-depth list, plan is to create a whole graph out of this! 110 134 fn render_sitemap_recursively(start_entry entry: Entry, level level: Int) { 111 - let children = list.filter(sitemap(), fn (maybe_child) {maybe_child.parent == entry.route}) 112 - html.li( 113 - [], 114 - [html.a([href(entry.route),attribute.class("link link-secondary-content ps-2")],[ 115 - element.text(entry.title) 116 - ]), 135 + let children = 136 + list.filter(sitemap(), fn(maybe_child) { maybe_child.parent == entry.route }) 137 + html.li([], [ 138 + html.a( 139 + [href(entry.route), attribute.class("link link-secondary-content ps-2")], 140 + [element.text(entry.title)], 141 + ), 117 142 118 - case children { 119 - [] -> element.none() 120 - _ -> { 121 - list.map(children, fn (child) { 122 - case child.route == Sitemap { 123 - False -> render_sitemap_recursively(child, level+1) 124 - // A CHILD OF MY OWN?! 125 - True -> html.li([], [html.span([attribute.class("text-info-content ps-2")], [element.text(child.title)]), element.text(" <--- You are here")]) 143 + case children { 144 + [] -> element.none() 145 + _ -> { 146 + list.map(children, fn(child) { 147 + case child.route == Sitemap { 148 + False -> render_sitemap_recursively(child, level + 1) 149 + // A CHILD OF MY OWN?! 150 + True -> 151 + html.li([], [ 152 + html.span([attribute.class("text-info-content ps-2")], [ 153 + element.text(child.title), 154 + ]), 155 + element.text(" <--- You are here"), 156 + ]) 157 + } 158 + }) 159 + |> html.ul( 160 + [ 161 + attribute.class( 162 + { 163 + case level, entry.route { 164 + 0, _ -> "list-['===>']" 165 + // Index 166 + 1, Posts -> "list-['----Post:']" 167 + 1, _ -> "list-['======>']" 168 + 2, _ -> "list-['=========>']" 169 + _, _ -> "list-disc" 170 + } 171 + } 172 + <> " list-inside mb-2", 173 + ), 174 + ], 175 + _, 176 + ) 126 177 } 127 - }) |> html.ul([attribute.class({ 128 - case level, entry.route { 129 - 0,_-> "list-['===>']" // Index 130 - 1,Posts -> "list-['----Post:']" 131 - 1,_-> "list-['======>']" 132 - 2,_-> "list-['=========>']" 133 - _,_ -> "list-disc" 134 - } 135 - } <> " list-inside mb-2")],_) 136 - } 137 - } 138 - 139 - ] 140 - ) 141 - 178 + }, 179 + ]) 142 180 } 143 - 144 - 145 181 146 182 const highlighted_posts = [900] 147 183 ··· 369 405 370 406 // Some types for your consideration 371 407 372 - pub type Entry{ 373 - Entry( 374 - route: Route, 375 - title: String, 376 - last_updated: calendar.Date, 377 - parent: Route 378 - ) 408 + pub type Entry { 409 + Entry(route: Route, title: String, last_updated: calendar.Date, parent: Route) 379 410 } 380 411 381 412 type Badge { ··· 592 623 } 593 624 } 594 625 595 - pub fn to_url (route: Route) -> String { 626 + pub fn to_url(route: Route) -> String { 596 627 case route { 597 628 Index -> "/" 598 629 Me -> "/me" ··· 621 652 Category(c) -> "/posts/category/" <> c 622 653 Sitemap -> "/sitemap" 623 654 } 624 - 625 655 } 626 656 627 657 fn href(route: Route) -> Attribute(Msg) { ··· 696 726 |> element.fragment 697 727 } 698 728 let into_main_with_badges = fn(content: List(Element(Msg))) -> Element(Msg) { 699 - let badges_ = list.map(badges |> list.shuffle(), render_badge) 729 + let badges_ = list.map(badges |> list.shuffle(), render_badge) 700 730 [ 701 731 content |> main(function.identity), 702 732 html.div( ··· 707 737 ], 708 738 case arrivertisements_show { 709 739 True -> { 710 - [ 711 - badges_ |> element.fragment(), 712 - // Arrivertisement! 713 - html.iframe([ 714 - attribute("loading", "lazy"), 715 - attribute.height(98), 716 - // attribute.width(722), 717 - attribute.class("border-none w-full mx-auto h-[98px] p-0 m-0"), 718 - attribute( 719 - "title", 720 - "An embed showing a silly image and author information.", 721 - ), 722 - attribute("referrerpolicy", "no-referrer"), 723 - attribute("sandbox", "allow-popups allow-popups-to-escape-sandbox"), 724 - attribute.src( 725 - "https://ad.ari.lt/ads/embed?from=strawmelonjuice.com", 726 - ), 727 - ]), 728 - ] 740 + [ 741 + badges_ |> element.fragment(), 742 + // Arrivertisement! 743 + html.iframe([ 744 + attribute("loading", "lazy"), 745 + attribute.height(98), 746 + // attribute.width(722), 747 + attribute.class("border-none w-full mx-auto h-[98px] p-0 m-0"), 748 + attribute( 749 + "title", 750 + "An embed showing a silly image and author information.", 751 + ), 752 + attribute("referrerpolicy", "no-referrer"), 753 + attribute( 754 + "sandbox", 755 + "allow-popups allow-popups-to-escape-sandbox", 756 + ), 757 + attribute.src( 758 + "https://ad.ari.lt/ads/embed?from=strawmelonjuice.com", 759 + ), 760 + ]), 761 + ] 729 762 } 730 763 False -> badges_ 731 - } 732 - 764 + }, 733 765 ), 734 766 html.hr([attribute.class("my-20 text-base-100")]), 735 767 ]
+73
written-contents/blog/personal/2026/nix.dj
··· 1 + # Mise in place 2 + 3 + I'm making some changes. 4 + 5 + [Mise-en-place](https://mise.jdx.dev/), for quite a while really was my holy grail. 6 + It handled everything: From tooling to taskrunning and from environment variables to editors. 7 + 8 + It got this position at first mostly because it, and its 9 + predecessor ASDF could flawlessly install the Gleam programming language tooling, as well as 10 + deal with other ecosystems very easily. 11 + 12 + ## Recent changes 13 + 14 + Recently, my digital life has been tumultuous. As a kid, I was used to Red Hat (not RHEL, 15 + Red Hat! Way before RHEL!). In my teen years, when I got into computing (and now with internet access) 16 + I started where lots of people started: Ubuntu and Mint. I have my opinions on that, btw: 17 + 18 + ``` =html 19 + <blockquote class="mastodon-embed" data-embed-url="https://pony.social/@strawmelonjuice/115913242078265620/embed" style="background: #FCF8FF; border-radius: 8px; border: 1px solid #C9C4DA; margin: 0; max-width: 540px; min-width: 270px; overflow: hidden; padding: 0;"> <a href="https://pony.social/@strawmelonjuice/115913242078265620" target="_blank" style="align-items: center; color: #1C1A25; display: flex; flex-direction: column; font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', Roboto, sans-serif; font-size: 14px; justify-content: center; letter-spacing: 0.25px; line-height: 20px; padding: 24px; text-decoration: none;"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 79 75"><path d="M63 45.3v-20c0-4.1-1-7.3-3.2-9.7-2.1-2.4-5-3.7-8.5-3.7-4.1 0-7.2 1.6-9.3 4.7l-2 3.3-2-3.3c-2-3.1-5.1-4.7-9.2-4.7-3.5 0-6.4 1.3-8.6 3.7-2.1 2.4-3.1 5.6-3.1 9.7v20h8V25.9c0-4.1 1.7-6.2 5.2-6.2 3.8 0 5.8 2.5 5.8 7.4V37.7H44V27.1c0-4.9 1.9-7.4 5.8-7.4 3.5 0 5.2 2.1 5.2 6.2V45.3h8ZM74.7 16.6c.6 6 .1 15.7.1 17.3 0 .5-.1 4.8-.1 5.3-.7 11.5-8 16-15.6 17.5-.1 0-.2 0-.3 0-4.9 1-10 1.2-14.9 1.4-1.2 0-2.4 0-3.6 0-4.8 0-9.7-.6-14.4-1.7-.1 0-.1 0-.1 0s-.1 0-.1 0 0 .1 0 .1 0 0 0 0c.1 1.6.4 3.1 1 4.5.6 1.7 2.9 5.7 11.4 5.7 5 0 9.9-.6 14.8-1.7 0 0 0 0 0 0 .1 0 .1 0 .1 0 0 .1 0 .1 0 .1.1 0 .1 0 .1.1v5.6s0 .1-.1.1c0 0 0 0 0 .1-1.6 1.1-3.7 1.7-5.6 2.3-.8.3-1.6.5-2.4.7-7.5 1.7-15.4 1.3-22.7-1.2-6.8-2.4-13.8-8.2-15.5-15.2-.9-3.8-1.6-7.6-1.9-11.5-.6-5.8-.6-11.7-.8-17.5C3.9 24.5 4 20 4.9 16 6.7 7.9 14.1 2.2 22.3 1c1.4-.2 4.1-1 16.5-1h.1C51.4 0 56.7.8 58.1 1c8.4 1.2 15.5 7.5 16.6 15.6Z" fill="currentColor"/></svg> <div style="color: #787588; margin-top: 16px;">Post by @strawmelonjuice@pony.social</div> <div style="font-weight: 500;">View on Mastodon</div> </a> </blockquote> <script data-allowed-prefixes="https://pony.social/" async src="https://pony.social/embed.js"></script> 20 + ``` 21 + 22 + For quite a while I felt like moving fast, maining Arch and making a Raspberry Pi 4, later 5 my main PC. This changed while 23 + being employed at ASML, I needed a bit more stability, but preferred it without stepping too far back. 24 + 25 + ### Stable on Fedora 26 + 27 + I landed at Fedora, comfortably fitting into exactly what I needed, later on started deepening further and got into the [immutable spins](https://fedoraproject.org/atomic-desktops/). 28 + 29 + This meant for a while that my [configurations](https://forge.strawmelonjuice.com/strawmelonjuice/dotfiles) were being perfected, with no change in the environment they lived in, well... They specialised further and further. 30 + 31 + ### Jujutsu 32 + 33 + JJ was in play already by this time, I had first heard of it about a year before joining ASML, but never actually gave it a shot. Within ASML I did see it's pro's, as I saw an entire team (not mine) struggle with merging a few changes. 34 + 35 + It actually took me about 6 months after that, while examining a friends (🧇) configurations, I decided to maybe copy parts from there onto my own. 36 + 37 + For me JJ did not solve any issues that Git had, because unlike what I hear from others, I've little headaches with Git. (I call skill issue on them :) However, JJ allows me to create a speedy and intuitive workflow, and I feel like it's efficiently hooking into the way I work. I am happy! 38 + 39 + ## Eyeing Nix 40 + 41 + Now, when rooting around in someone elses dotfiles, especially if they were already written in Nixlang, for [Home manager](https://nix-community.github.io/home-manager/index.xhtml#ch-introduction)... That rarely leaves no trace on your own ways! I was curious, and getting bored with Fedora. 42 + 43 + In the span of 4 days and a total of 36 hours configuring (so jobless, I know... I'm sorry) I had my [dotfiles-nix-branch](https://forge.strawmelonjuice.com/strawmelonjuice/dotfiles/src/branch/nix) ready for takeoff! It just worked, with some exceptions and some hacky tricks, but it was so _smoooootth_, I am sold. 44 + 45 + ### Playing nice with Mise 46 + 47 + Mise-en-place and Nix are very different in the way they do things. And this is fine, until they clash. In fact, it was fine for me until I opened a big project and my terminal literally got stuck in a loop, with both Mise and nix-shell trying to set their environment up. 48 + 49 + This is when I decided to remove the auto-activation of Mise from my shell config, and... explore. 50 + 51 + ### `flake.nix` 52 + 53 + You set up an environment using a `flake.nix` file, which then for my case usually specifies some packages to install for this project, and lists some tasks you can run. 54 + 55 + In mise you would specify the dependencies you need and these would just be shimmed or path-linked into your shell, for Nix you start a dev-shell using `nix develop`. The extra step can be annoying, could also be auto-ran for me by my already quite advanced fish-cd config, but I like it. It keeps me conscious. 56 + 57 + Running tasks, as I said earlier can still be done using mise! But in most cases, switching to Just was now... more justified. The fact I have this choice now, does feel liberating. Using mise for everything it'd make no sense at all to also use Just, but now that I lay my eggs in two baskets, may as well be three. 58 + 59 + I also feel like I'm far less bloated by all this. Both Nix and Just run with a command and try to stay out of your way every other moment. Even though I switched to more tools, there's no tool to force me into it's ways now. 60 + 61 + This goes for Git/JJ too! One example is that the [CynthiaMini](https://github.com/CynthiaWebsiteEngine/Mini) is still quite used to just working with Git, and I do prefer it there. 62 + 63 + ## Conclusion: The Intentional Kitchen 64 + 65 + It’s ironic, isn't it? I moved away from a "do-it-all" tool to a stack of three or four separate ones, yet my processes feel much lighter. 66 + 67 + By separating my version control (JJ), my environment (Nix), and my tasks (Just), I’ve traded "magic" for "intent." My computer still does all the cool little tricks I want it to do, but on my call, not on that of a tool which should really not be in charge (Mise). 68 + 69 + I’m still a fan of Mise—it’s a phenomenal piece of engineering. But as my workflow has matured (and my OS has become immutable), I’ve realized that I don't need a holy grail. I just need a set of sharp, predictable tools that stay out of each other's way. 70 + 71 + My digital life might have been tumultuous lately, but for the first time in a long time, the "place" in my mise en place feels permanent. 72 + 73 + P.S. Mise-en-place documentation is starting to feel increasingly more incorrect and Chatbot-like... This also tipped me over the edge, but I am not one for negative driving powers. :)