···3434tom = ">= 2.0.0 and < 3.0.0"
3535gleam_javascript = ">= 1.0.0 and < 2.0.0"
3636envoy = ">= 1.1.0 and < 2.0.0"
3737+atomb = ">= 1.0.0 and < 2.0.0"
3838+youid = ">= 1.5.1 and < 2.0.0"
+5
manifest.toml
···22# You typically do not need to edit this file
3344packages = [
55+ { name = "atomb", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_time", "xmb"], otp_app = "atomb", source = "hex", outer_checksum = "5BA533CDB8F29ECD8FF08835A79DBE6E0724E7D2C5D515898C3DA9B9D006DEA0" },
56 { name = "envoy", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "850DA9D29D2E5987735872A2B5C81035146D7FE19EFC486129E44440D03FD832" },
67 { name = "exception", version = "2.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "329D269D5C2A314F7364BD2711372B6F2C58FA6F39981572E5CA68624D291F8C" },
78 { name = "filepath", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "B06A9AF0BF10E51401D64B98E4B627F1D2E48C154967DA7AF4D0914780A6D40A" },
···2122 { name = "splitter", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "splitter", source = "hex", outer_checksum = "3DFD6B6C49E61EDAF6F7B27A42054A17CFF6CA2135FF553D0CB61C234D281DD0" },
2223 { name = "temporary", version = "1.0.0", build_tools = ["gleam"], requirements = ["envoy", "exception", "filepath", "gleam_crypto", "gleam_stdlib", "simplifile"], otp_app = "temporary", source = "hex", outer_checksum = "51C0FEF4D72CE7CA507BD188B21C1F00695B3D5B09D7DFE38240BFD3A8E1E9B3" },
2324 { name = "tom", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_time"], otp_app = "tom", source = "hex", outer_checksum = "74D0C5A3761F7A7D06994755D4D5AD854122EF8E9F9F76A3E7547606D8C77091" },
2525+ { name = "xmb", version = "1.4.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "xmb", source = "hex", outer_checksum = "1C4B67ACBFE703122414691A63DC3591AE6FCACF7DC13A78A1C1E96D7B56B757" },
2626+ { name = "youid", version = "1.5.1", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_stdlib", "gleam_time"], otp_app = "youid", source = "hex", outer_checksum = "580E909FD704DB16416D5CB080618EDC2DA0F1BE4D21B490C0683335E3FFC5AF" },
2427]
25282629[requirements]
3030+atomb = { version = ">= 1.0.0 and < 2.0.0" }
2731envoy = { version = ">= 1.1.0 and < 2.0.0" }
2832gleam_javascript = { version = ">= 1.0.0 and < 2.0.0" }
2933gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" }
···3337lustre_ssg = { git = "https://github.com/fruno-bulax/lustre_ssg", ref = "6c132bd34ab75a1144d31c0f896cab0e3cbf80fc" }
3438simplifile = { version = ">= 2.3.2 and < 3.0.0" }
3539tom = { version = ">= 2.0.0 and < 3.0.0" }
4040+youid = { version = ">= 1.5.1 and < 2.0.0" }
+2
posts/2026-01-03-initial-commit.djot
···11---
22title = "making a blog for dummies"
33listed = false
44+published = 2026-01-11T12:46:00+02:00
55+summary = "This will be a summary."
46---
5768# this is an example for a draft!
+28-2
src/blog.gleam
···22import gleam/list
33import gleam/result
44import gleam/string
55+import gleam/time/timestamp.{type Timestamp}
56import lustre/attribute
67import lustre/element.{type Element}
78import lustre/element/html
···1011import tom
11121213pub type Post {
1313- Post(slug: String, title: String, content: String, listed: Bool)
1414+ Post(
1515+ slug: String,
1616+ title: String,
1717+ published: Timestamp,
1818+ updated: Timestamp,
1919+ summary: String,
2020+ content: String,
2121+ listed: Bool,
2222+ )
1423}
15241625const posts_dir = "./posts"
···3241 as "Failed to read file"
3342 let assert Ok(meta) = djot.metadata(content) as "Failed to read post metadata"
3443 let assert Ok(title) = tom.get_string(meta, ["title"]) as "Missing post title"
4444+ let assert Ok(summary) = tom.get_string(meta, ["summary"])
4545+ as "Missing post summary"
4646+4747+ let assert Ok(#(date, time, tom.Offset(offset))) =
4848+ tom.get_calendar_time(meta, ["published"])
4949+ as "No publication timestamp or missing offset"
5050+ let published = timestamp.from_calendar(date, time, offset)
5151+ let updated = case tom.get(meta, ["updated"]) {
5252+ Ok(updated) -> {
5353+ let assert Ok(#(date, time, tom.Offset(offset))) =
5454+ tom.as_calendar_time(updated)
5555+ as "Invalid updated timestamp"
5656+ timestamp.from_calendar(date, time, offset)
5757+ }
5858+ Error(_) -> published
5959+ }
6060+3561 let listed = result.unwrap(tom.get_bool(meta, ["listed"]), True)
36623737- Post(slug:, title:, content:, listed:)
6363+ Post(slug:, title:, summary:, published:, updated:, content:, listed:)
3864}
39654066pub fn list_posts(posts: List(Post)) -> List(Element(msg)) {