tangled
alpha
login
or
join now
kacaii.dev
/
blog
0
fork
atom
💻 My personal website
blog.kacaii.dev/
blog
gleam
lustre
0
fork
atom
overview
issues
pulls
pipelines
:zap: render home as a static page
kacaii.dev
2 months ago
d8e49c75
d955f41b
+70
-45
15 changed files
expand all
collapse all
unified
split
.gitignore
.justfiles
tailwind.just
priv
static
style
blog.css
font
MapleMono-NF-Bold.ttf
MapleMono-NF-BoldItalic.ttf
MapleMono-NF-Italic.ttf
MapleMono-NF-Regular.ttf
font.css
src
blog
page
home.gleam
navbar.gleam
post.gleam
root.gleam
blog.gleam
build.gleam
web
http_router.gleam
+2
-1
.gitignore
···
14
14
/.lustre
15
15
/dist
16
16
17
17
-
priv/static/posts/
17
17
+
priv/static/*.html
18
18
+
priv/static/posts/*.html
+1
-1
.justfiles/tailwind.just
···
1
1
set quiet := true
2
2
3
3
input_css := "input.css"
4
4
-
output_css := "priv" / "static" / "output.css"
4
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
24
-
fn read_posts(priv: String) -> Result(List(post.Post), post.PostError) {
24
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
8
-
import wisp
9
8
10
10
-
pub fn handle_request(ctx: web.Context) -> wisp.Response {
9
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
13
-
let elements = [
14
14
-
recent_posts.view(ctx.posts),
15
15
-
]
12
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
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
9
-
route(icon: "nf-fa-home", href: "/", label: "Home"),
9
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
2
+
import contour
2
3
import frontmatter
3
4
import gleam/int
5
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
11
+
import lustre/attribute.{class}
12
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
43
+
}
44
44
+
}
45
45
+
46
46
+
pub fn view(post: Post) -> element.Element(a) {
47
47
+
let content =
48
48
+
post.body.content
49
49
+
|> list.map(highlight_codeblock)
50
50
+
51
51
+
let post_body =
52
52
+
jot.Document(..post.body, content:)
53
53
+
|> jot.document_to_html
54
54
+
55
55
+
let style = class("grid grid-cols-1 gap-4 w-full text-pretty post-content")
56
56
+
57
57
+
element.fragment([element.unsafe_raw_html("", "article", [style], post_body)])
58
58
+
}
59
59
+
60
60
+
fn highlight_codeblock(container: jot.Container) -> jot.Container {
61
61
+
case container {
62
62
+
jot.Codeblock(_, language: option.Some("gleam"), content:) -> {
63
63
+
let code = "<pre><code>" <> contour.to_html(content) <> "</code></pre>"
64
64
+
jot.RawBlock(code)
65
65
+
}
66
66
+
67
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
17
-
html.link([attr.rel("stylesheet"), attr.href("/blog.css")]),
17
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
3
+
import blog/page/home
3
4
import blog/page/navbar
4
5
import blog/post
5
6
import blog/root
6
6
-
import contour
7
7
import filepath as path
8
8
import gleam/list
9
9
-
import gleam/option
10
10
-
import jot
11
9
import lustre/attribute.{class} as attr
12
12
-
import lustre/element
13
10
import lustre/element/html
14
11
import simplifile
12
12
+
import web
15
13
16
16
-
pub fn main() {
14
14
+
fn with_context(next: fn(web.Context) -> Nil) -> Nil {
17
15
let assert Ok(priv) = blog.priv_directory()
16
16
+
let assert Ok(posts) = blog.read_posts(priv)
17
17
+
let ctx = web.Context(priv:, posts:)
18
18
+
19
19
+
next(ctx)
20
20
+
}
21
21
+
22
22
+
pub fn main() -> Nil {
23
23
+
use ctx <- with_context()
24
24
+
25
25
+
build_home(ctx)
26
26
+
build_posts(ctx.priv)
27
27
+
}
28
28
+
29
29
+
fn build_home(ctx: web.Context) -> Nil {
30
30
+
let out_file =
31
31
+
path.join(ctx.priv, "static")
32
32
+
|> path.join("index.html")
33
33
+
34
34
+
let html = home.view(ctx)
35
35
+
let assert Ok(_) = simplifile.write(out_file, html)
36
36
+
37
37
+
Nil
38
38
+
}
39
39
+
40
40
+
fn build_posts(priv: String) -> Nil {
18
41
let posts_path = path.join(priv, "posts")
19
19
-
let out_path = path.join(priv, "static") |> path.join("posts")
42
42
+
let out_path =
43
43
+
path.join(priv, "static")
44
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
51
-
view(post),
76
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
59
-
60
60
-
pub fn view(post: post.Post) -> element.Element(a) {
61
61
-
let content =
62
62
-
post.body.content
63
63
-
|> list.map(highlight_codeblock)
64
64
-
65
65
-
let post_body =
66
66
-
jot.Document(..post.body, content:)
67
67
-
|> jot.document_to_html
68
68
-
69
69
-
let style = class("grid grid-cols-1 gap-4 w-full text-pretty post-content")
70
70
-
71
71
-
element.fragment([element.unsafe_raw_html("", "article", [style], post_body)])
72
72
-
}
73
73
-
74
74
-
fn highlight_codeblock(container: jot.Container) -> jot.Container {
75
75
-
case container {
76
76
-
jot.Codeblock(_, language: option.Some("gleam"), content:) -> {
77
77
-
let code = "<pre><code>" <> contour.to_html(content) <> "</code></pre>"
78
78
-
jot.RawBlock(code)
79
79
-
}
80
80
-
81
81
-
other -> other
82
82
-
}
83
83
-
}
+1
-2
src/web/http_router.gleam
···
1
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
10
-
[] -> home.handle_request(ctx)
9
9
+
[] -> wisp.redirect("/index.html")
11
10
["posts"] -> posts.handle_request(ctx)
12
11
13
12
["healthcheck"] -> wisp.ok()