💻 My personal website blog.kacaii.dev/
blog gleam lustre

:zap: render home as a static page

+70 -45
+2 -1
.gitignore
··· 14 14 /.lustre 15 15 /dist 16 16 17 - priv/static/posts/ 17 + priv/static/*.html 18 + priv/static/posts/*.html
+1 -1
.justfiles/tailwind.just
··· 1 1 set quiet := true 2 2 3 3 input_css := "input.css" 4 - output_css := "priv" / "static" / "output.css" 4 + output_css := "priv" / "static" / "style" / "output.css" 5 5 6 6 #  Start Tailwind file watching 7 7 [no-cd]
priv/static/blog.css priv/static/style/blog.css
priv/static/font.css priv/static/style/font.css
priv/static/font/MapleMono-NF-Bold.ttf priv/static/style/font/MapleMono-NF-Bold.ttf
priv/static/font/MapleMono-NF-BoldItalic.ttf priv/static/style/font/MapleMono-NF-BoldItalic.ttf
priv/static/font/MapleMono-NF-Italic.ttf priv/static/style/font/MapleMono-NF-Italic.ttf
priv/static/font/MapleMono-NF-Regular.ttf priv/static/style/font/MapleMono-NF-Regular.ttf
+1 -1
src/blog.gleam
··· 21 21 process.sleep_forever() 22 22 } 23 23 24 - fn read_posts(priv: String) -> Result(List(post.Post), post.PostError) { 24 + pub fn read_posts(priv: String) -> Result(List(post.Post), post.PostError) { 25 25 let posts_path = path.join(priv, "posts") 26 26 let assert Ok(entries) = simplifile.read_directory(posts_path) 27 27 as "Read posts directory"
+2 -6
src/blog/page/home.gleam
··· 5 5 import lustre/attribute.{class} 6 6 import lustre/element/html 7 7 import web 8 - import wisp 9 8 10 - pub fn handle_request(ctx: web.Context) -> wisp.Response { 9 + pub fn view(ctx: web.Context) { 11 10 let main_style = "grid grid-cols-1 gap-4 mx-auto w-full" 12 11 13 - let elements = [ 14 - recent_posts.view(ctx.posts), 15 - ] 12 + let elements = [recent_posts.view(ctx.posts)] 16 13 17 14 let content = [ 18 15 navbar.view([]), ··· 21 18 ] 22 19 23 20 root.view(content:, title: "Home") 24 - |> wisp.html_response(200) 25 21 }
+1 -1
src/blog/page/navbar.gleam
··· 6 6 let style = class("grid grid-cols-3 place-items-center") 7 7 8 8 html.nav([style, ..attributes], [ 9 - route(icon: "nf-fa-home", href: "/", label: "Home"), 9 + route(icon: "nf-fa-home", href: "/index.html", label: "Home"), 10 10 route(icon: "nf-md-file_document_edit", href: "/posts", label: "Articles"), 11 11 route(icon: "nf-md-file_star", href: "/posts/uses.html", label: "Uses"), 12 12 ])
+29
src/blog/post.gleam
··· 1 1 import blog/post/metadata 2 + import contour 2 3 import frontmatter 3 4 import gleam/int 5 + import gleam/list 4 6 import gleam/option 5 7 import gleam/order 6 8 import gleam/result 7 9 import gleam/time/calendar 8 10 import jot 11 + import lustre/attribute.{class} 12 + import lustre/element 9 13 import simplifile 10 14 11 15 pub opaque type PostError { ··· 36 40 37 41 Post(meta:, body: jot.parse(content)) 38 42 } 43 + } 44 + } 45 + 46 + pub fn view(post: Post) -> element.Element(a) { 47 + let content = 48 + post.body.content 49 + |> list.map(highlight_codeblock) 50 + 51 + let post_body = 52 + jot.Document(..post.body, content:) 53 + |> jot.document_to_html 54 + 55 + let style = class("grid grid-cols-1 gap-4 w-full text-pretty post-content") 56 + 57 + element.fragment([element.unsafe_raw_html("", "article", [style], post_body)]) 58 + } 59 + 60 + fn highlight_codeblock(container: jot.Container) -> jot.Container { 61 + case container { 62 + jot.Codeblock(_, language: option.Some("gleam"), content:) -> { 63 + let code = "<pre><code>" <> contour.to_html(content) <> "</code></pre>" 64 + jot.RawBlock(code) 65 + } 66 + 67 + other -> other 39 68 } 40 69 } 41 70
+1 -1
src/blog/root.gleam
··· 14 14 html.meta([attr.charset("utf-8")]), 15 15 html.title([], title), 16 16 viewport_meta, 17 - html.link([attr.rel("stylesheet"), attr.href("/blog.css")]), 17 + html.link([attr.rel("stylesheet"), attr.href("/style/blog.css")]), 18 18 ]) 19 19 20 20 let style =
+32 -32
src/build.gleam
··· 1 1 import blog 2 2 import blog/page/footer 3 + import blog/page/home 3 4 import blog/page/navbar 4 5 import blog/post 5 6 import blog/root 6 - import contour 7 7 import filepath as path 8 8 import gleam/list 9 - import gleam/option 10 - import jot 11 9 import lustre/attribute.{class} as attr 12 - import lustre/element 13 10 import lustre/element/html 14 11 import simplifile 12 + import web 15 13 16 - pub fn main() { 14 + fn with_context(next: fn(web.Context) -> Nil) -> Nil { 17 15 let assert Ok(priv) = blog.priv_directory() 16 + let assert Ok(posts) = blog.read_posts(priv) 17 + let ctx = web.Context(priv:, posts:) 18 + 19 + next(ctx) 20 + } 21 + 22 + pub fn main() -> Nil { 23 + use ctx <- with_context() 24 + 25 + build_home(ctx) 26 + build_posts(ctx.priv) 27 + } 28 + 29 + fn build_home(ctx: web.Context) -> Nil { 30 + let out_file = 31 + path.join(ctx.priv, "static") 32 + |> path.join("index.html") 33 + 34 + let html = home.view(ctx) 35 + let assert Ok(_) = simplifile.write(out_file, html) 36 + 37 + Nil 38 + } 39 + 40 + fn build_posts(priv: String) -> Nil { 18 41 let posts_path = path.join(priv, "posts") 19 - let out_path = path.join(priv, "static") |> path.join("posts") 42 + let out_path = 43 + path.join(priv, "static") 44 + |> path.join("posts") 20 45 21 46 let assert Ok(entries) = simplifile.read_directory(posts_path) 22 47 as "Read posts directory" ··· 48 73 root.view(title:, content: [ 49 74 navbar.view([]), 50 75 post_header, 51 - view(post), 76 + post.view(post), 52 77 back_button, 53 78 footer.view(), 54 79 ]) ··· 56 81 let out_file = path.join(out_path, post.meta.slug <> ".html") 57 82 let assert Ok(_) = simplifile.write(out_file, content_html) 58 83 } 59 - 60 - pub fn view(post: post.Post) -> element.Element(a) { 61 - let content = 62 - post.body.content 63 - |> list.map(highlight_codeblock) 64 - 65 - let post_body = 66 - jot.Document(..post.body, content:) 67 - |> jot.document_to_html 68 - 69 - let style = class("grid grid-cols-1 gap-4 w-full text-pretty post-content") 70 - 71 - element.fragment([element.unsafe_raw_html("", "article", [style], post_body)]) 72 - } 73 - 74 - fn highlight_codeblock(container: jot.Container) -> jot.Container { 75 - case container { 76 - jot.Codeblock(_, language: option.Some("gleam"), content:) -> { 77 - let code = "<pre><code>" <> contour.to_html(content) <> "</code></pre>" 78 - jot.RawBlock(code) 79 - } 80 - 81 - other -> other 82 - } 83 - }
+1 -2
src/web/http_router.gleam
··· 1 - import blog/page/home 2 1 import blog/page/posts 3 2 import web 4 3 import wisp ··· 7 6 use req <- web.middleware(req, ctx) 8 7 9 8 case wisp.path_segments(req) { 10 - [] -> home.handle_request(ctx) 9 + [] -> wisp.redirect("/index.html") 11 10 ["posts"] -> posts.handle_request(ctx) 12 11 13 12 ["healthcheck"] -> wisp.ok()