My personal website, in gleam+lustre!
0
fork

Configure Feed

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

Prepped a post.


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

+137 -4
+6
site.css
··· 86 86 } 87 87 } 88 88 89 + .codeblock { 90 + font-family: 91 + "Lilex", 92 + monospace; 93 + } 94 + 89 95 /* .subpage {} */ 90 96 91 97 /* Make sure the 'go comment' button is visible even on small screens. */
+25
src/homepage.gleam
··· 45 45 comments: CommentsDisable, 46 46 ), 47 47 Post( 48 + id: 5, 49 + category: "Devlog", 50 + title: "Chilp", 51 + summary: "Bluesky and Mastodon comments on my blog!", 52 + published: Date(2026, April, 23) |> stuff.no_time_timestamp(), 53 + revised: None, 54 + body: File(Djot, "./written-contents/blog/devblog/chilp/chilp-2.dj"), 55 + tags: [ 56 + "web", 57 + "social media", 58 + "lustre", 59 + "gleam", 60 + "blogging", 61 + "indie web", 62 + "indieweb", 63 + "coding", 64 + "software engineering", 65 + "technology", 66 + "personal blog", 67 + ".blog-personal", 68 + ], 69 + aliases: ["chilp-2-announcement", "chilpv2"], 70 + comments: CommentsDisable, 71 + ), 72 + Post( 48 73 3, 49 74 category: "Devlog", 50 75 title: "I lied",
+4 -4
src/homepage/djotparse.gleam
··· 335 335 } 336 336 337 337 fn process_codeblock(collected: String, grammar: Grammar) -> String { 338 - "\n\n``` =html\n<pre class=\"bg-slate-500 rounded-sm text-primary p-1 m-0\"><code class=\"codeblock\">" 339 - <> "<span class=\"text-sm float-right\">" 338 + "\n\n``` =html\n<pre class=\"bg-slate-500 rounded-sm text-primary p-1 m-0 relative\">" 339 + <> "<span class=\"text-sm right-1 top-1 absolute\">" 340 340 <> grammar.name 341 341 |> string.capitalise() 342 - <> "</span>" 342 + <> "</span><code class=\"codeblock\"><div class=\"overflow-y-auto\">" 343 343 <> smalto.to_html(collected, grammar) 344 344 |> string.replace("smalto-function", "text-[#9ce7ff] p-0 m-0") 345 345 |> string.replace("smalto-punctuation", "p-0 m-0") ··· 354 354 |> string.replace("smalto-attribute", "text-[#ffd596] p-0 m-0") 355 355 |> string.replace("smalto-selector", "text-[#9ce7ff] p-0 m-0") 356 356 |> string.replace("smalto-property", "text-[#ffd596] p-0 m-0") 357 - <> "\n</code></pre>\n\n```" 357 + <> "\n</code></div></pre>\n\n```" 358 358 } 359 359 360 360 fn find_codeblock_grammar(t: String) -> Option(Grammar) {
+102
written-contents/blog/devblog/chilp/chilp-2.dj
··· 1 + # Chilp! 2 + 3 + Yes! Based on the sound a bird makes! 4 + 5 + Chilp allows you to get a nice little comment section on your static blog, completely client-side! How? Well, as often: thievery! 6 + 7 + ...okay, let me go a little slower. Maybe you've seen those blogs before, where you needed GitHub to comment? They've likely been 8 + using [Utterances](https://utteranc.es/), which creates a GitHub issue, and then renders the comments on it as comments on your post. 9 + 10 + This is super duper cool, I used this too! However, if you're a bit less of a developer, making a GitHub account feels cumbersome just to comment on a blog post. 11 + ... On the other hand, if you ARE very into tech you may just be like me, and annoyed at how GitHub handles things privacy and performance-wise. 12 + 13 + Now, I'm not the first to do this, I've seen others use Bluesky for their comment backends as well. However, being a [Gleamling](https://gleam.run/) and avid fan of the [Lustre framework](https://hexdocs.pm/lustre/guide/01-quickstart.html), well! 14 + 15 + So, with v1 I was still very heavily trying to understand the Mastodon API and what it told me. Then, I created Chilp with some... Unconventionalities, which meant I didn't feel like highlighting it too much. 16 + ... BUT! 17 + 18 + ## Chilp version 2 19 + 20 + Okay, the intro is done, let me show you how easy it is for me to insert a Chilp widget. 21 + 22 + This is just the example from <https://github.com/lustre-labs/lustre/blob/main/examples/01-basics/01-hello-world/src/app.gleam>, I added only about 20 lines to it! 23 + 24 + ### An example 25 + 26 + 27 + ```gleam 28 + // IMPORTS -------------------------------------------------------------------------------------------------------------- 29 + import lustre 30 + import lustre/element.{type Element} 31 + import lustre/element/html 32 + import lustre/event 33 + import chilp // <--- Imports Chilp the ergonomic way, if you want less imports, 34 + import chilp/widget // /-------- going with only `chilp/widget` is okay too! Then you'd need to 35 + import chilp/widget/anchors // <- make sure your anchor is sanely split using a backslash though. 36 + 37 + // MAIN ----------------------------------------------------------------------------------------------------------------- 38 + 39 + pub fn main() { 40 + let assert Ok(_) = widget.register() 41 + let app = lustre.simple(init, update, view) 42 + let assert Ok(_) = lustre.start(app, "#app", Nil) 43 + 44 + Nil 45 + } 46 + 47 + type Model = 48 + Int 49 + 50 + fn init(_) -> Model { 51 + 0 52 + } 53 + 54 + // UPDATE -------------------------------------------------------------------------------------------------------------- 55 + 56 + type Message { 57 + UserClickedIncrement 58 + UserClickedDecrement 59 + } 60 + 61 + fn update(model: Model, message: Message) -> Model { 62 + case message { 63 + UserClickedIncrement -> model + 1 64 + UserClickedDecrement -> model - 1 65 + } 66 + } 67 + 68 + // VIEW ----------------------------------------------------------------------------------------------------------------- 69 + 70 + fn view(model: Model) -> Element(Message) { 71 + let count = int.to_string(model) 72 + 73 + html.div([], [ 74 + html.button([event.on_click(UserClickedDecrement)], [html.text("-")]), 75 + html.p([], [html.text("Count: "), html.text(count)]), 76 + html.button([event.on_click(UserClickedIncrement)], [html.text("+")]), 77 + // And then we place the widget itself here! under the counter. 78 + chilp.widget( 79 + // Anchoring this one to me mewing on the timeline 80 + option.Some(anchors.Mastodon( 81 + instance: "pony.social", 82 + postid: "115911235653686237", 83 + )), 84 + // And anchoring this one to a post about my hamster 85 + option.Some(anchors.Bluesky( 86 + did: "did:plc:jgtfsmv25thfs4zmydtbccnn", 87 + postid: "3mjeg3m7fd22i", 88 + )), 89 + ), 90 + ]) 91 + } 92 + ``` 93 + 94 + The widget will be unstyled but is very easily styled into shape using for example DaisyUI, if you just want to see it alive, see the bottom of this post! 95 + 96 + ### What it does 97 + 98 + Well, as one would expect, Chilp fetches your posts from the anchored platforms, then lists the comments under them and renders them into view. 99 + It also creates the well known 'enter your instance address' for Mastodon users to respond from the instance of their choice, while showing buttons 100 + to the favourite appviews for Bluesky users to instantly respond! 101 + 102 + Let's not keep on nerding now, just AMA in the comments below!