+2
gleam.toml
+2
gleam.toml
+2
manifest.toml
+2
manifest.toml
···
23
23
]
24
24
25
25
[requirements]
26
+
gleam_regexp = { version = ">= 1.1.1 and < 2.0.0" }
26
27
gleam_stdlib = { version = ">= 0.60.0 and < 0.70.0" }
27
28
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
29
+
jot = { version = ">= 4.0.0 and < 5.0.0" }
28
30
lustre = { version = ">= 5.0.0 and < 6.0.0" }
29
31
lustre_ssg = { version = ">= 0.8.3 and < 1.0.0" }
30
32
simplifile = { version = ">= 2.0.0 and < 3.0.0" }
+44
-6
src/website/common.gleam
+44
-6
src/website/common.gleam
···
5
5
import lustre/element/html
6
6
import lustre/ssg/djot
7
7
import tom
8
+
9
+
// import website/djot_renderer
8
10
import website/style
9
11
10
12
pub type Post(a) {
···
60
62
]),
61
63
html.footer([style.footer()], [
62
64
html.hr([attr.style("color", "lightgrey")]),
63
-
.."
64
-
The source code for this website is available [here](https://codeberg.org/naomi/website) or [here](https://tangled.sh/@lesbian.skin/website) under the [BSD 3 with Attribution](https://spdx.org/licenses/BSD-3-Clause-Attribution) license.
65
-
66
-
If you wish to get in contact, please send an email to [mia@naomieow.xyz](mailto:mia@naomieow.xyz), or contact me on [Discord](https://chat.lesbian.skin).
67
-
"
68
-
|> djot.render(djot.default_renderer())
65
+
html.p([], [
66
+
html.text("The source code for this website is available "),
67
+
link([], "here", "https://codeberg.org/naomi/website", True),
68
+
html.text(" or "),
69
+
link([], "here", "https://tangled.sh/@lesbian.skin/website", True),
70
+
html.text(" under the "),
71
+
link(
72
+
[],
73
+
"BSD 3 with Attribution",
74
+
"https://spdx.org/licenses/BSD-3-Clause-Attribution",
75
+
True,
76
+
),
77
+
html.text(" license."),
78
+
]),
79
+
html.p([], [
80
+
html.text("If you wish to get in contact, please send an email to "),
81
+
link([], "mia@naomieow.xyz", "mailto:mia@naomieow.xyz", True),
82
+
html.text(", or contact me on "),
83
+
link([], "Discord", "https://chat.lesbian.skin", True),
84
+
html.text("."),
85
+
]),
69
86
]),
70
87
])
71
88
}
···
146
163
// False -> element.none()
147
164
// },
148
165
],
166
+
)
167
+
}
168
+
169
+
pub fn raw_link(
170
+
attributes attributes: List(attr.Attribute(a)),
171
+
content content: List(element.Element(a)),
172
+
href href: String,
173
+
external external: Bool,
174
+
) -> element.Element(a) {
175
+
let attrs = [attr.href(href), style.link(), ..attributes]
176
+
html.a(
177
+
case external {
178
+
False -> attrs
179
+
True -> [
180
+
attr.target("_blank"),
181
+
attr.rel("noopener noreferrer"),
182
+
attr.style("display", "inline-block"),
183
+
..attrs
184
+
]
185
+
},
186
+
content,
149
187
)
150
188
}
151
189
+2
-1
src/website/data/posts.gleam
+2
-1
src/website/data/posts.gleam
···
7
7
import simplifile
8
8
import tom
9
9
import website/common.{type Post, Post}
10
+
import website/djot_renderer
10
11
11
12
pub fn all() -> List(Post(a)) {
12
13
let path = "./src/website/data/posts/"
···
32
33
[],
33
34
post_md
34
35
|> djot.content()
35
-
|> djot.render(djot.default_renderer()),
36
+
|> djot.render(djot_renderer.renderer()),
36
37
),
37
38
)
38
39
})
+2
-1
src/website/data/projects.gleam
+2
-1
src/website/data/projects.gleam
···
7
7
import simplifile
8
8
import tom
9
9
import website/common.{type Project, Link, Project}
10
+
import website/djot_renderer
10
11
11
12
pub fn all() -> List(Project(a)) {
12
13
let path = "./src/website/data/projects/"
···
56
57
[],
57
58
project_md
58
59
|> djot.content()
59
-
|> djot.render(djot.default_renderer()),
60
+
|> djot.render(djot_renderer.renderer()),
60
61
),
61
62
)
62
63
})
+104
src/website/djot_renderer.gleam
+104
src/website/djot_renderer.gleam
···
1
+
import gleam/dict
2
+
import gleam/list
3
+
import gleam/option
4
+
import gleam/regexp
5
+
import gleam/string
6
+
import jot
7
+
import lustre/attribute as attr
8
+
import lustre/element
9
+
import lustre/element/html
10
+
import lustre/ssg/djot
11
+
import website/common
12
+
import website/style
13
+
14
+
pub fn renderer() -> djot.Renderer(element.Element(msg)) {
15
+
let to_attributes = fn(attrs) {
16
+
use attrs, key, val <- dict.fold(attrs, [])
17
+
[attr.attribute(key, val), ..attrs]
18
+
}
19
+
20
+
djot.Renderer(
21
+
codeblock: fn(attrs, lang, code) {
22
+
let lang = option.unwrap(lang, "text")
23
+
html.pre([style.code_block(), ..to_attributes(attrs)], [
24
+
html.code([attr.attribute("data-lang", lang), style.monospaced()], [
25
+
html.text(code),
26
+
]),
27
+
])
28
+
},
29
+
emphasis: fn(content) { html.em([], content) },
30
+
heading: fn(attrs, level, content) {
31
+
case level {
32
+
1 -> html.h1(to_attributes(attrs), content)
33
+
2 -> html.h2(to_attributes(attrs), content)
34
+
3 -> html.h3(to_attributes(attrs), content)
35
+
4 -> html.h4(to_attributes(attrs), content)
36
+
5 -> html.h5(to_attributes(attrs), content)
37
+
6 -> html.h6(to_attributes(attrs), content)
38
+
_ -> html.p(to_attributes(attrs), content)
39
+
}
40
+
},
41
+
link: fn(destination, references, content) {
42
+
case destination {
43
+
jot.Reference(ref) ->
44
+
case dict.get(references, ref) {
45
+
Ok(url) ->
46
+
common.raw_link(
47
+
attributes: [],
48
+
content:,
49
+
href: url,
50
+
external: False,
51
+
)
52
+
Error(_) ->
53
+
common.raw_link(
54
+
attributes: [attr.id(linkify("back-to-" <> ref))],
55
+
content:,
56
+
href: "#" <> linkify(ref),
57
+
external: False,
58
+
)
59
+
}
60
+
jot.Url(url) ->
61
+
common.raw_link(attributes: [], content:, href: url, external: False)
62
+
}
63
+
},
64
+
paragraph: fn(attrs, content) { html.p(to_attributes(attrs), content) },
65
+
bullet_list: fn(layout, style, items) {
66
+
let list_style_type =
67
+
attr.style("list-style-type", case style {
68
+
"-" -> "'-'"
69
+
"*" -> "disc"
70
+
_ -> "circle"
71
+
})
72
+
73
+
html.ul([list_style_type], {
74
+
list.map(items, fn(item) {
75
+
case layout {
76
+
jot.Tight -> html.li([], item)
77
+
jot.Loose -> html.li([], [html.p([], item)])
78
+
}
79
+
})
80
+
})
81
+
},
82
+
raw_html: fn(content) { element.unsafe_raw_html("", "div", [], content) },
83
+
strong: fn(content) { html.strong([], content) },
84
+
text: fn(text) { html.text(text) },
85
+
code: fn(content) { html.code([style.monospaced()], [html.text(content)]) },
86
+
image: fn(destination, alt) {
87
+
case destination {
88
+
jot.Reference(ref) ->
89
+
html.img([attr.src("#" <> linkify(ref)), attr.alt(alt)])
90
+
jot.Url(url) -> html.img([attr.src(url), attr.alt(alt)])
91
+
}
92
+
},
93
+
linebreak: html.br([]),
94
+
thematicbreak: html.hr([]),
95
+
)
96
+
}
97
+
98
+
fn linkify(text: String) -> String {
99
+
let assert Ok(re) = regexp.from_string(" +")
100
+
101
+
text
102
+
|> regexp.split(re, _)
103
+
|> string.join("-")
104
+
}
+3
-2
src/website/page/post.gleam
+3
-2
src/website/page/post.gleam
···
4
4
import lustre/ssg/djot
5
5
import website/common.{type Post}
6
6
import website/data/posts
7
+
import website/djot_renderer
7
8
import website/style
8
9
9
10
pub fn view_all(posts: List(Post(a))) -> List(Element(a)) {
···
32
33
]),
33
34
]),
34
35
html.div([], [
35
-
html.p([], post.summary |> djot.render(djot.default_renderer())),
36
+
html.p([], post.summary |> djot.render(djot_renderer.renderer())),
36
37
]),
37
38
])
38
39
})
···
57
58
]),
58
59
]),
59
60
html.hr([]),
60
-
html.p([], post.summary |> djot.render(djot.default_renderer())),
61
+
html.p([], post.summary |> djot.render(djot_renderer.renderer())),
61
62
post.content,
62
63
]),
63
64
]
+3
-2
src/website/page/project.gleam
+3
-2
src/website/page/project.gleam
···
5
5
import lustre/ssg/djot
6
6
import website/common.{type Project, Project}
7
7
import website/data/projects
8
+
import website/djot_renderer
8
9
import website/style
9
10
10
11
pub fn view_all(projects: List(Project(a))) -> List(Element(a)) {
···
35
36
),
36
37
]),
37
38
html.div([], [
38
-
html.p([], project.summary |> djot.render(djot.default_renderer())),
39
+
html.p([], project.summary |> djot.render(djot_renderer.renderer())),
39
40
]),
40
41
])
41
42
})
···
64
65
}),
65
66
),
66
67
]),
67
-
html.p([], project.summary |> djot.render(djot.default_renderer())),
68
+
html.p([], project.summary |> djot.render(djot_renderer.renderer())),
68
69
project.content,
69
70
]),
70
71
]
+14
-1
src/website/style.gleam
+14
-1
src/website/style.gleam
···
120
120
pub fn link() {
121
121
attr.styles([
122
122
#("text-decoration", "underline"),
123
-
#("color", "black"),
123
+
#("color", "darkblue"),
124
124
#("font-variant-emoji", "text"),
125
125
])
126
126
}
···
195
195
#("gap", "0.5em"),
196
196
])
197
197
}
198
+
199
+
pub fn monospaced() {
200
+
attr.styles([#("font-family", "monospace, monospace")])
201
+
}
202
+
203
+
pub fn code_block() {
204
+
attr.styles([
205
+
#("font-family", "monospace, monospace"),
206
+
#("border-radius", "0.4em"),
207
+
#("border", "2px solid black"),
208
+
#("padding", "1em 1em 1em 1em"),
209
+
])
210
+
}